Roo/HtmlEditorCore.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
650 /*
651  * Based on:
652  * Ext JS Library 1.1.1
653  * Copyright(c) 2006-2007, Ext JS, LLC.
654  *
655  * Originally Released Under LGPL - original licence link has changed is not relivant.
656  *
657  * Fork - LGPL
658  * <script type="text/javascript">
659  */
660
661 (function() {    
662     // wrappedn so fnCleanup is not in global scope...
663     if(Roo.isIE) {
664         function fnCleanUp() {
665             var p = Function.prototype;
666             delete p.createSequence;
667             delete p.defer;
668             delete p.createDelegate;
669             delete p.createCallback;
670             delete p.createInterceptor;
671
672             window.detachEvent("onunload", fnCleanUp);
673         }
674         window.attachEvent("onunload", fnCleanUp);
675     }
676 })();
677
678
679 /**
680  * @class Function
681  * These functions are available on every Function object (any JavaScript function).
682  */
683 Roo.apply(Function.prototype, {
684      /**
685      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
686      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
687      * Will create a function that is bound to those 2 args.
688      * @return {Function} The new function
689     */
690     createCallback : function(/*args...*/){
691         // make args available, in function below
692         var args = arguments;
693         var method = this;
694         return function() {
695             return method.apply(window, args);
696         };
697     },
698
699     /**
700      * Creates a delegate (callback) that sets the scope to obj.
701      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
702      * Will create a function that is automatically scoped to this.
703      * @param {Object} obj (optional) The object for which the scope is set
704      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
705      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
706      *                                             if a number the args are inserted at the specified position
707      * @return {Function} The new function
708      */
709     createDelegate : function(obj, args, appendArgs){
710         var method = this;
711         return function() {
712             var callArgs = args || arguments;
713             if(appendArgs === true){
714                 callArgs = Array.prototype.slice.call(arguments, 0);
715                 callArgs = callArgs.concat(args);
716             }else if(typeof appendArgs == "number"){
717                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
718                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
719                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
720             }
721             return method.apply(obj || window, callArgs);
722         };
723     },
724
725     /**
726      * Calls this function after the number of millseconds specified.
727      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
728      * @param {Object} obj (optional) The object for which the scope is set
729      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
730      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
731      *                                             if a number the args are inserted at the specified position
732      * @return {Number} The timeout id that can be used with clearTimeout
733      */
734     defer : function(millis, obj, args, appendArgs){
735         var fn = this.createDelegate(obj, args, appendArgs);
736         if(millis){
737             return setTimeout(fn, millis);
738         }
739         fn();
740         return 0;
741     },
742     /**
743      * Create a combined function call sequence of the original function + the passed function.
744      * The resulting function returns the results of the original function.
745      * The passed fcn is called with the parameters of the original function
746      * @param {Function} fcn The function to sequence
747      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
748      * @return {Function} The new function
749      */
750     createSequence : function(fcn, scope){
751         if(typeof fcn != "function"){
752             return this;
753         }
754         var method = this;
755         return function() {
756             var retval = method.apply(this || window, arguments);
757             fcn.apply(scope || this || window, arguments);
758             return retval;
759         };
760     },
761
762     /**
763      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function.
766      * @addon
767      * @param {Function} fcn The function to call before the original
768      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
769      * @return {Function} The new function
770      */
771     createInterceptor : function(fcn, scope){
772         if(typeof fcn != "function"){
773             return this;
774         }
775         var method = this;
776         return function() {
777             fcn.target = this;
778             fcn.method = method;
779             if(fcn.apply(scope || this || window, arguments) === false){
780                 return;
781             }
782             return method.apply(this || window, arguments);
783         };
784     }
785 });
786 /*
787  * Based on:
788  * Ext JS Library 1.1.1
789  * Copyright(c) 2006-2007, Ext JS, LLC.
790  *
791  * Originally Released Under LGPL - original licence link has changed is not relivant.
792  *
793  * Fork - LGPL
794  * <script type="text/javascript">
795  */
796
797 Roo.applyIf(String, {
798     
799     /** @scope String */
800     
801     /**
802      * Escapes the passed string for ' and \
803      * @param {String} string The string to escape
804      * @return {String} The escaped string
805      * @static
806      */
807     escape : function(string) {
808         return string.replace(/('|\\)/g, "\\$1");
809     },
810
811     /**
812      * Pads the left side of a string with a specified character.  This is especially useful
813      * for normalizing number and date strings.  Example usage:
814      * <pre><code>
815 var s = String.leftPad('123', 5, '0');
816 // s now contains the string: '00123'
817 </code></pre>
818      * @param {String} string The original string
819      * @param {Number} size The total length of the output string
820      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
821      * @return {String} The padded string
822      * @static
823      */
824     leftPad : function (val, size, ch) {
825         var result = new String(val);
826         if(ch === null || ch === undefined || ch === '') {
827             ch = " ";
828         }
829         while (result.length < size) {
830             result = ch + result;
831         }
832         return result;
833     },
834
835     /**
836      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
837      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
838      * <pre><code>
839 var cls = 'my-class', text = 'Some text';
840 var s = String.format('<div class="{0}">{1}</div>', cls, text);
841 // s now contains the string: '<div class="my-class">Some text</div>'
842 </code></pre>
843      * @param {String} string The tokenized string to be formatted
844      * @param {String} value1 The value to replace token {0}
845      * @param {String} value2 Etc...
846      * @return {String} The formatted string
847      * @static
848      */
849     format : function(format){
850         var args = Array.prototype.slice.call(arguments, 1);
851         return format.replace(/\{(\d+)\}/g, function(m, i){
852             return Roo.util.Format.htmlEncode(args[i]);
853         });
854     }
855 });
856
857 /**
858  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
859  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
860  * they are already different, the first value passed in is returned.  Note that this method returns the new value
861  * but does not change the current string.
862  * <pre><code>
863 // alternate sort directions
864 sort = sort.toggle('ASC', 'DESC');
865
866 // instead of conditional logic:
867 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
868 </code></pre>
869  * @param {String} value The value to compare to the current string
870  * @param {String} other The new value to use if the string already equals the first value passed in
871  * @return {String} The new value
872  */
873  
874 String.prototype.toggle = function(value, other){
875     return this == value ? other : value;
876 };/*
877  * Based on:
878  * Ext JS Library 1.1.1
879  * Copyright(c) 2006-2007, Ext JS, LLC.
880  *
881  * Originally Released Under LGPL - original licence link has changed is not relivant.
882  *
883  * Fork - LGPL
884  * <script type="text/javascript">
885  */
886
887  /**
888  * @class Number
889  */
890 Roo.applyIf(Number.prototype, {
891     /**
892      * Checks whether or not the current number is within a desired range.  If the number is already within the
893      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
894      * exceeded.  Note that this method returns the constrained value but does not change the current number.
895      * @param {Number} min The minimum number in the range
896      * @param {Number} max The maximum number in the range
897      * @return {Number} The constrained value if outside the range, otherwise the current value
898      */
899     constrain : function(min, max){
900         return Math.min(Math.max(this, min), max);
901     }
902 });/*
903  * Based on:
904  * Ext JS Library 1.1.1
905  * Copyright(c) 2006-2007, Ext JS, LLC.
906  *
907  * Originally Released Under LGPL - original licence link has changed is not relivant.
908  *
909  * Fork - LGPL
910  * <script type="text/javascript">
911  */
912  /**
913  * @class Array
914  */
915 Roo.applyIf(Array.prototype, {
916     /**
917      * Checks whether or not the specified object exists in the array.
918      * @param {Object} o The object to check for
919      * @return {Number} The index of o in the array (or -1 if it is not found)
920      */
921     indexOf : function(o){
922        for (var i = 0, len = this.length; i < len; i++){
923               if(this[i] == o) return i;
924        }
925            return -1;
926     },
927
928     /**
929      * Removes the specified object from the array.  If the object is not found nothing happens.
930      * @param {Object} o The object to remove
931      */
932     remove : function(o){
933        var index = this.indexOf(o);
934        if(index != -1){
935            this.splice(index, 1);
936        }
937     },
938     /**
939      * Map (JS 1.6 compatibility)
940      * @param {Function} function  to call
941      */
942     map : function(fun )
943     {
944         var len = this.length >>> 0;
945         if (typeof fun != "function")
946             throw new TypeError();
947
948         var res = new Array(len);
949         var thisp = arguments[1];
950         for (var i = 0; i < len; i++)
951         {
952             if (i in this)
953                 res[i] = fun.call(thisp, this[i], i, this);
954         }
955
956         return res;
957     }
958     
959 });
960
961
962  /*
963  * Based on:
964  * Ext JS Library 1.1.1
965  * Copyright(c) 2006-2007, Ext JS, LLC.
966  *
967  * Originally Released Under LGPL - original licence link has changed is not relivant.
968  *
969  * Fork - LGPL
970  * <script type="text/javascript">
971  */
972
973 /**
974  * @class Date
975  *
976  * The date parsing and format syntax is a subset of
977  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
978  * supported will provide results equivalent to their PHP versions.
979  *
980  * Following is the list of all currently supported formats:
981  *<pre>
982 Sample date:
983 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
984
985 Format  Output      Description
986 ------  ----------  --------------------------------------------------------------
987   d      10         Day of the month, 2 digits with leading zeros
988   D      Wed        A textual representation of a day, three letters
989   j      10         Day of the month without leading zeros
990   l      Wednesday  A full textual representation of the day of the week
991   S      th         English ordinal day of month suffix, 2 chars (use with j)
992   w      3          Numeric representation of the day of the week
993   z      9          The julian date, or day of the year (0-365)
994   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
995   F      January    A full textual representation of the month
996   m      01         Numeric representation of a month, with leading zeros
997   M      Jan        Month name abbreviation, three letters
998   n      1          Numeric representation of a month, without leading zeros
999   t      31         Number of days in the given month
1000   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1001   Y      2007       A full numeric representation of a year, 4 digits
1002   y      07         A two digit representation of a year
1003   a      pm         Lowercase Ante meridiem and Post meridiem
1004   A      PM         Uppercase Ante meridiem and Post meridiem
1005   g      3          12-hour format of an hour without leading zeros
1006   G      15         24-hour format of an hour without leading zeros
1007   h      03         12-hour format of an hour with leading zeros
1008   H      15         24-hour format of an hour with leading zeros
1009   i      05         Minutes with leading zeros
1010   s      01         Seconds, with leading zeros
1011   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1012   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1013   T      CST        Timezone setting of the machine running the code
1014   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1015 </pre>
1016  *
1017  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1018  * <pre><code>
1019 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1020 document.write(dt.format('Y-m-d'));                         //2007-01-10
1021 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1022 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1023  </code></pre>
1024  *
1025  * Here are some standard date/time patterns that you might find helpful.  They
1026  * are not part of the source of Date.js, but to use them you can simply copy this
1027  * block of code into any script that is included after Date.js and they will also become
1028  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1029  * <pre><code>
1030 Date.patterns = {
1031     ISO8601Long:"Y-m-d H:i:s",
1032     ISO8601Short:"Y-m-d",
1033     ShortDate: "n/j/Y",
1034     LongDate: "l, F d, Y",
1035     FullDateTime: "l, F d, Y g:i:s A",
1036     MonthDay: "F d",
1037     ShortTime: "g:i A",
1038     LongTime: "g:i:s A",
1039     SortableDateTime: "Y-m-d\\TH:i:s",
1040     UniversalSortableDateTime: "Y-m-d H:i:sO",
1041     YearMonth: "F, Y"
1042 };
1043 </code></pre>
1044  *
1045  * Example usage:
1046  * <pre><code>
1047 var dt = new Date();
1048 document.write(dt.format(Date.patterns.ShortDate));
1049  </code></pre>
1050  */
1051
1052 /*
1053  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1054  * They generate precompiled functions from date formats instead of parsing and
1055  * processing the pattern every time you format a date.  These functions are available
1056  * on every Date object (any javascript function).
1057  *
1058  * The original article and download are here:
1059  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1060  *
1061  */
1062  
1063  
1064  // was in core
1065 /**
1066  Returns the number of milliseconds between this date and date
1067  @param {Date} date (optional) Defaults to now
1068  @return {Number} The diff in milliseconds
1069  @member Date getElapsed
1070  */
1071 Date.prototype.getElapsed = function(date) {
1072         return Math.abs((date || new Date()).getTime()-this.getTime());
1073 };
1074 // was in date file..
1075
1076
1077 // private
1078 Date.parseFunctions = {count:0};
1079 // private
1080 Date.parseRegexes = [];
1081 // private
1082 Date.formatFunctions = {count:0};
1083
1084 // private
1085 Date.prototype.dateFormat = function(format) {
1086     if (Date.formatFunctions[format] == null) {
1087         Date.createNewFormat(format);
1088     }
1089     var func = Date.formatFunctions[format];
1090     return this[func]();
1091 };
1092
1093
1094 /**
1095  * Formats a date given the supplied format string
1096  * @param {String} format The format string
1097  * @return {String} The formatted date
1098  * @method
1099  */
1100 Date.prototype.format = Date.prototype.dateFormat;
1101
1102 // private
1103 Date.createNewFormat = function(format) {
1104     var funcName = "format" + Date.formatFunctions.count++;
1105     Date.formatFunctions[format] = funcName;
1106     var code = "Date.prototype." + funcName + " = function(){return ";
1107     var special = false;
1108     var ch = '';
1109     for (var i = 0; i < format.length; ++i) {
1110         ch = format.charAt(i);
1111         if (!special && ch == "\\") {
1112             special = true;
1113         }
1114         else if (special) {
1115             special = false;
1116             code += "'" + String.escape(ch) + "' + ";
1117         }
1118         else {
1119             code += Date.getFormatCode(ch);
1120         }
1121     }
1122     /** eval:var:zzzzzzzzzzzzz */
1123     eval(code.substring(0, code.length - 3) + ";}");
1124 };
1125
1126 // private
1127 Date.getFormatCode = function(character) {
1128     switch (character) {
1129     case "d":
1130         return "String.leftPad(this.getDate(), 2, '0') + ";
1131     case "D":
1132         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1133     case "j":
1134         return "this.getDate() + ";
1135     case "l":
1136         return "Date.dayNames[this.getDay()] + ";
1137     case "S":
1138         return "this.getSuffix() + ";
1139     case "w":
1140         return "this.getDay() + ";
1141     case "z":
1142         return "this.getDayOfYear() + ";
1143     case "W":
1144         return "this.getWeekOfYear() + ";
1145     case "F":
1146         return "Date.monthNames[this.getMonth()] + ";
1147     case "m":
1148         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1149     case "M":
1150         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1151     case "n":
1152         return "(this.getMonth() + 1) + ";
1153     case "t":
1154         return "this.getDaysInMonth() + ";
1155     case "L":
1156         return "(this.isLeapYear() ? 1 : 0) + ";
1157     case "Y":
1158         return "this.getFullYear() + ";
1159     case "y":
1160         return "('' + this.getFullYear()).substring(2, 4) + ";
1161     case "a":
1162         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1163     case "A":
1164         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1165     case "g":
1166         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1167     case "G":
1168         return "this.getHours() + ";
1169     case "h":
1170         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1171     case "H":
1172         return "String.leftPad(this.getHours(), 2, '0') + ";
1173     case "i":
1174         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1175     case "s":
1176         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1177     case "O":
1178         return "this.getGMTOffset() + ";
1179     case "P":
1180         return "this.getGMTColonOffset() + ";
1181     case "T":
1182         return "this.getTimezone() + ";
1183     case "Z":
1184         return "(this.getTimezoneOffset() * -60) + ";
1185     default:
1186         return "'" + String.escape(character) + "' + ";
1187     }
1188 };
1189
1190 /**
1191  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1192  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1193  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1194  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1195  * string or the parse operation will fail.
1196  * Example Usage:
1197 <pre><code>
1198 //dt = Fri May 25 2007 (current date)
1199 var dt = new Date();
1200
1201 //dt = Thu May 25 2006 (today's month/day in 2006)
1202 dt = Date.parseDate("2006", "Y");
1203
1204 //dt = Sun Jan 15 2006 (all date parts specified)
1205 dt = Date.parseDate("2006-1-15", "Y-m-d");
1206
1207 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1208 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1209 </code></pre>
1210  * @param {String} input The unparsed date as a string
1211  * @param {String} format The format the date is in
1212  * @return {Date} The parsed date
1213  * @static
1214  */
1215 Date.parseDate = function(input, format) {
1216     if (Date.parseFunctions[format] == null) {
1217         Date.createParser(format);
1218     }
1219     var func = Date.parseFunctions[format];
1220     return Date[func](input);
1221 };
1222 /**
1223  * @private
1224  */
1225 Date.createParser = function(format) {
1226     var funcName = "parse" + Date.parseFunctions.count++;
1227     var regexNum = Date.parseRegexes.length;
1228     var currentGroup = 1;
1229     Date.parseFunctions[format] = funcName;
1230
1231     var code = "Date." + funcName + " = function(input){\n"
1232         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1233         + "var d = new Date();\n"
1234         + "y = d.getFullYear();\n"
1235         + "m = d.getMonth();\n"
1236         + "d = d.getDate();\n"
1237         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1238         + "if (results && results.length > 0) {";
1239     var regex = "";
1240
1241     var special = false;
1242     var ch = '';
1243     for (var i = 0; i < format.length; ++i) {
1244         ch = format.charAt(i);
1245         if (!special && ch == "\\") {
1246             special = true;
1247         }
1248         else if (special) {
1249             special = false;
1250             regex += String.escape(ch);
1251         }
1252         else {
1253             var obj = Date.formatCodeToRegex(ch, currentGroup);
1254             currentGroup += obj.g;
1255             regex += obj.s;
1256             if (obj.g && obj.c) {
1257                 code += obj.c;
1258             }
1259         }
1260     }
1261
1262     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i, s);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1265         + "{v = new Date(y, m, d, h, i);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1267         + "{v = new Date(y, m, d, h);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1269         + "{v = new Date(y, m, d);}\n"
1270         + "else if (y >= 0 && m >= 0)\n"
1271         + "{v = new Date(y, m);}\n"
1272         + "else if (y >= 0)\n"
1273         + "{v = new Date(y);}\n"
1274         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1275         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1276         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1277         + ";}";
1278
1279     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1280     /** eval:var:zzzzzzzzzzzzz */
1281     eval(code);
1282 };
1283
1284 // private
1285 Date.formatCodeToRegex = function(character, currentGroup) {
1286     switch (character) {
1287     case "D":
1288         return {g:0,
1289         c:null,
1290         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1291     case "j":
1292         return {g:1,
1293             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1294             s:"(\\d{1,2})"}; // day of month without leading zeroes
1295     case "d":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{2})"}; // day of month with leading zeroes
1299     case "l":
1300         return {g:0,
1301             c:null,
1302             s:"(?:" + Date.dayNames.join("|") + ")"};
1303     case "S":
1304         return {g:0,
1305             c:null,
1306             s:"(?:st|nd|rd|th)"};
1307     case "w":
1308         return {g:0,
1309             c:null,
1310             s:"\\d"};
1311     case "z":
1312         return {g:0,
1313             c:null,
1314             s:"(?:\\d{1,3})"};
1315     case "W":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{2})"};
1319     case "F":
1320         return {g:1,
1321             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1322             s:"(" + Date.monthNames.join("|") + ")"};
1323     case "M":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1326             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1327     case "n":
1328         return {g:1,
1329             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1330             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1331     case "m":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1335     case "t":
1336         return {g:0,
1337             c:null,
1338             s:"\\d{1,2}"};
1339     case "L":
1340         return {g:0,
1341             c:null,
1342             s:"(?:1|0)"};
1343     case "Y":
1344         return {g:1,
1345             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1346             s:"(\\d{4})"};
1347     case "y":
1348         return {g:1,
1349             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1350                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1351             s:"(\\d{1,2})"};
1352     case "a":
1353         return {g:1,
1354             c:"if (results[" + currentGroup + "] == 'am') {\n"
1355                 + "if (h == 12) { h = 0; }\n"
1356                 + "} else { if (h < 12) { h += 12; }}",
1357             s:"(am|pm)"};
1358     case "A":
1359         return {g:1,
1360             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1361                 + "if (h == 12) { h = 0; }\n"
1362                 + "} else { if (h < 12) { h += 12; }}",
1363             s:"(AM|PM)"};
1364     case "g":
1365     case "G":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1369     case "h":
1370     case "H":
1371         return {g:1,
1372             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1373             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1374     case "i":
1375         return {g:1,
1376             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"};
1378     case "s":
1379         return {g:1,
1380             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "O":
1383         return {g:1,
1384             c:[
1385                 "o = results[", currentGroup, "];\n",
1386                 "var sn = o.substring(0,1);\n", // get + / - sign
1387                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1388                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1389                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1390                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1391             ].join(""),
1392             s:"([+\-]\\d{2,4})"};
1393     
1394     
1395     case "P":
1396         return {g:1,
1397                 c:[
1398                    "o = results[", currentGroup, "];\n",
1399                    "var sn = o.substring(0,1);\n",
1400                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1401                    "var mn = o.substring(4,6) % 60;\n",
1402                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1403                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1404             ].join(""),
1405             s:"([+\-]\\d{4})"};
1406     case "T":
1407         return {g:0,
1408             c:null,
1409             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1410     case "Z":
1411         return {g:1,
1412             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1413                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1414             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1415     default:
1416         return {g:0,
1417             c:null,
1418             s:String.escape(character)};
1419     }
1420 };
1421
1422 /**
1423  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1424  * @return {String} The abbreviated timezone name (e.g. 'CST')
1425  */
1426 Date.prototype.getTimezone = function() {
1427     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1428 };
1429
1430 /**
1431  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1432  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1433  */
1434 Date.prototype.getGMTOffset = function() {
1435     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1436         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1437         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1438 };
1439
1440 /**
1441  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1442  * @return {String} 2-characters representing hours and 2-characters representing minutes
1443  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1444  */
1445 Date.prototype.getGMTColonOffset = function() {
1446         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1447                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1448                 + ":"
1449                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1450 }
1451
1452 /**
1453  * Get the numeric day number of the year, adjusted for leap year.
1454  * @return {Number} 0 through 364 (365 in leap years)
1455  */
1456 Date.prototype.getDayOfYear = function() {
1457     var num = 0;
1458     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1459     for (var i = 0; i < this.getMonth(); ++i) {
1460         num += Date.daysInMonth[i];
1461     }
1462     return num + this.getDate() - 1;
1463 };
1464
1465 /**
1466  * Get the string representation of the numeric week number of the year
1467  * (equivalent to the format specifier 'W').
1468  * @return {String} '00' through '52'
1469  */
1470 Date.prototype.getWeekOfYear = function() {
1471     // Skip to Thursday of this week
1472     var now = this.getDayOfYear() + (4 - this.getDay());
1473     // Find the first Thursday of the year
1474     var jan1 = new Date(this.getFullYear(), 0, 1);
1475     var then = (7 - jan1.getDay() + 4);
1476     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1477 };
1478
1479 /**
1480  * Whether or not the current date is in a leap year.
1481  * @return {Boolean} True if the current date is in a leap year, else false
1482  */
1483 Date.prototype.isLeapYear = function() {
1484     var year = this.getFullYear();
1485     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1486 };
1487
1488 /**
1489  * Get the first day of the current month, adjusted for leap year.  The returned value
1490  * is the numeric day index within the week (0-6) which can be used in conjunction with
1491  * the {@link #monthNames} array to retrieve the textual day name.
1492  * Example:
1493  *<pre><code>
1494 var dt = new Date('1/10/2007');
1495 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1496 </code></pre>
1497  * @return {Number} The day number (0-6)
1498  */
1499 Date.prototype.getFirstDayOfMonth = function() {
1500     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1501     return (day < 0) ? (day + 7) : day;
1502 };
1503
1504 /**
1505  * Get the last day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getLastDayOfMonth = function() {
1516     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520
1521 /**
1522  * Get the first date of this date's month
1523  * @return {Date}
1524  */
1525 Date.prototype.getFirstDateOfMonth = function() {
1526     return new Date(this.getFullYear(), this.getMonth(), 1);
1527 };
1528
1529 /**
1530  * Get the last date of this date's month
1531  * @return {Date}
1532  */
1533 Date.prototype.getLastDateOfMonth = function() {
1534     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1535 };
1536 /**
1537  * Get the number of days in the current month, adjusted for leap year.
1538  * @return {Number} The number of days in the month
1539  */
1540 Date.prototype.getDaysInMonth = function() {
1541     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1542     return Date.daysInMonth[this.getMonth()];
1543 };
1544
1545 /**
1546  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1547  * @return {String} 'st, 'nd', 'rd' or 'th'
1548  */
1549 Date.prototype.getSuffix = function() {
1550     switch (this.getDate()) {
1551         case 1:
1552         case 21:
1553         case 31:
1554             return "st";
1555         case 2:
1556         case 22:
1557             return "nd";
1558         case 3:
1559         case 23:
1560             return "rd";
1561         default:
1562             return "th";
1563     }
1564 };
1565
1566 // private
1567 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1568
1569 /**
1570  * An array of textual month names.
1571  * Override these values for international dates, for example...
1572  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1573  * @type Array
1574  * @static
1575  */
1576 Date.monthNames =
1577    ["January",
1578     "February",
1579     "March",
1580     "April",
1581     "May",
1582     "June",
1583     "July",
1584     "August",
1585     "September",
1586     "October",
1587     "November",
1588     "December"];
1589
1590 /**
1591  * An array of textual day names.
1592  * Override these values for international dates, for example...
1593  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1594  * @type Array
1595  * @static
1596  */
1597 Date.dayNames =
1598    ["Sunday",
1599     "Monday",
1600     "Tuesday",
1601     "Wednesday",
1602     "Thursday",
1603     "Friday",
1604     "Saturday"];
1605
1606 // private
1607 Date.y2kYear = 50;
1608 // private
1609 Date.monthNumbers = {
1610     Jan:0,
1611     Feb:1,
1612     Mar:2,
1613     Apr:3,
1614     May:4,
1615     Jun:5,
1616     Jul:6,
1617     Aug:7,
1618     Sep:8,
1619     Oct:9,
1620     Nov:10,
1621     Dec:11};
1622
1623 /**
1624  * Creates and returns a new Date instance with the exact same date value as the called instance.
1625  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1626  * variable will also be changed.  When the intention is to create a new variable that will not
1627  * modify the original instance, you should create a clone.
1628  *
1629  * Example of correctly cloning a date:
1630  * <pre><code>
1631 //wrong way:
1632 var orig = new Date('10/1/2006');
1633 var copy = orig;
1634 copy.setDate(5);
1635 document.write(orig);  //returns 'Thu Oct 05 2006'!
1636
1637 //correct way:
1638 var orig = new Date('10/1/2006');
1639 var copy = orig.clone();
1640 copy.setDate(5);
1641 document.write(orig);  //returns 'Thu Oct 01 2006'
1642 </code></pre>
1643  * @return {Date} The new Date instance
1644  */
1645 Date.prototype.clone = function() {
1646         return new Date(this.getTime());
1647 };
1648
1649 /**
1650  * Clears any time information from this date
1651  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1652  @return {Date} this or the clone
1653  */
1654 Date.prototype.clearTime = function(clone){
1655     if(clone){
1656         return this.clone().clearTime();
1657     }
1658     this.setHours(0);
1659     this.setMinutes(0);
1660     this.setSeconds(0);
1661     this.setMilliseconds(0);
1662     return this;
1663 };
1664
1665 // private
1666 // safari setMonth is broken
1667 if(Roo.isSafari){
1668     Date.brokenSetMonth = Date.prototype.setMonth;
1669         Date.prototype.setMonth = function(num){
1670                 if(num <= -1){
1671                         var n = Math.ceil(-num);
1672                         var back_year = Math.ceil(n/12);
1673                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1674                         this.setFullYear(this.getFullYear() - back_year);
1675                         return Date.brokenSetMonth.call(this, month);
1676                 } else {
1677                         return Date.brokenSetMonth.apply(this, arguments);
1678                 }
1679         };
1680 }
1681
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.MILLI = "ms";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.SECOND = "s";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.MINUTE = "mi";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.HOUR = "h";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.DAY = "d";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MONTH = "mo";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.YEAR = "y";
1710
1711 /**
1712  * Provides a convenient method of performing basic date arithmetic.  This method
1713  * does not modify the Date instance being called - it creates and returns
1714  * a new Date instance containing the resulting date value.
1715  *
1716  * Examples:
1717  * <pre><code>
1718 //Basic usage:
1719 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1720 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1721
1722 //Negative values will subtract correctly:
1723 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1724 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1725
1726 //You can even chain several calls together in one line!
1727 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1728 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1729  </code></pre>
1730  *
1731  * @param {String} interval   A valid date interval enum value
1732  * @param {Number} value      The amount to add to the current date
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.add = function(interval, value){
1736   var d = this.clone();
1737   if (!interval || value === 0) return d;
1738   switch(interval.toLowerCase()){
1739     case Date.MILLI:
1740       d.setMilliseconds(this.getMilliseconds() + value);
1741       break;
1742     case Date.SECOND:
1743       d.setSeconds(this.getSeconds() + value);
1744       break;
1745     case Date.MINUTE:
1746       d.setMinutes(this.getMinutes() + value);
1747       break;
1748     case Date.HOUR:
1749       d.setHours(this.getHours() + value);
1750       break;
1751     case Date.DAY:
1752       d.setDate(this.getDate() + value);
1753       break;
1754     case Date.MONTH:
1755       var day = this.getDate();
1756       if(day > 28){
1757           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1758       }
1759       d.setDate(day);
1760       d.setMonth(this.getMonth() + value);
1761       break;
1762     case Date.YEAR:
1763       d.setFullYear(this.getFullYear() + value);
1764       break;
1765   }
1766   return d;
1767 };
1768 /*
1769  * Based on:
1770  * Ext JS Library 1.1.1
1771  * Copyright(c) 2006-2007, Ext JS, LLC.
1772  *
1773  * Originally Released Under LGPL - original licence link has changed is not relivant.
1774  *
1775  * Fork - LGPL
1776  * <script type="text/javascript">
1777  */
1778
1779 /**
1780  * @class Roo.lib.Dom
1781  * @static
1782  * 
1783  * Dom utils (from YIU afaik)
1784  * 
1785  **/
1786 Roo.lib.Dom = {
1787     /**
1788      * Get the view width
1789      * @param {Boolean} full True will get the full document, otherwise it's the view width
1790      * @return {Number} The width
1791      */
1792      
1793     getViewWidth : function(full) {
1794         return full ? this.getDocumentWidth() : this.getViewportWidth();
1795     },
1796     /**
1797      * Get the view height
1798      * @param {Boolean} full True will get the full document, otherwise it's the view height
1799      * @return {Number} The height
1800      */
1801     getViewHeight : function(full) {
1802         return full ? this.getDocumentHeight() : this.getViewportHeight();
1803     },
1804
1805     getDocumentHeight: function() {
1806         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1807         return Math.max(scrollHeight, this.getViewportHeight());
1808     },
1809
1810     getDocumentWidth: function() {
1811         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1812         return Math.max(scrollWidth, this.getViewportWidth());
1813     },
1814
1815     getViewportHeight: function() {
1816         var height = self.innerHeight;
1817         var mode = document.compatMode;
1818
1819         if ((mode || Roo.isIE) && !Roo.isOpera) {
1820             height = (mode == "CSS1Compat") ?
1821                      document.documentElement.clientHeight :
1822                      document.body.clientHeight;
1823         }
1824
1825         return height;
1826     },
1827
1828     getViewportWidth: function() {
1829         var width = self.innerWidth;
1830         var mode = document.compatMode;
1831
1832         if (mode || Roo.isIE) {
1833             width = (mode == "CSS1Compat") ?
1834                     document.documentElement.clientWidth :
1835                     document.body.clientWidth;
1836         }
1837         return width;
1838     },
1839
1840     isAncestor : function(p, c) {
1841         p = Roo.getDom(p);
1842         c = Roo.getDom(c);
1843         if (!p || !c) {
1844             return false;
1845         }
1846
1847         if (p.contains && !Roo.isSafari) {
1848             return p.contains(c);
1849         } else if (p.compareDocumentPosition) {
1850             return !!(p.compareDocumentPosition(c) & 16);
1851         } else {
1852             var parent = c.parentNode;
1853             while (parent) {
1854                 if (parent == p) {
1855                     return true;
1856                 }
1857                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1858                     return false;
1859                 }
1860                 parent = parent.parentNode;
1861             }
1862             return false;
1863         }
1864     },
1865
1866     getRegion : function(el) {
1867         return Roo.lib.Region.getRegion(el);
1868     },
1869
1870     getY : function(el) {
1871         return this.getXY(el)[1];
1872     },
1873
1874     getX : function(el) {
1875         return this.getXY(el)[0];
1876     },
1877
1878     getXY : function(el) {
1879         var p, pe, b, scroll, bd = document.body;
1880         el = Roo.getDom(el);
1881         var fly = Roo.lib.AnimBase.fly;
1882         if (el.getBoundingClientRect) {
1883             b = el.getBoundingClientRect();
1884             scroll = fly(document).getScroll();
1885             return [b.left + scroll.left, b.top + scroll.top];
1886         }
1887         var x = 0, y = 0;
1888
1889         p = el;
1890
1891         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1892
1893         while (p) {
1894
1895             x += p.offsetLeft;
1896             y += p.offsetTop;
1897
1898             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1899                 hasAbsolute = true;
1900             }
1901
1902             if (Roo.isGecko) {
1903                 pe = fly(p);
1904
1905                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1906                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1907
1908
1909                 x += bl;
1910                 y += bt;
1911
1912
1913                 if (p != el && pe.getStyle('overflow') != 'visible') {
1914                     x += bl;
1915                     y += bt;
1916                 }
1917             }
1918             p = p.offsetParent;
1919         }
1920
1921         if (Roo.isSafari && hasAbsolute) {
1922             x -= bd.offsetLeft;
1923             y -= bd.offsetTop;
1924         }
1925
1926         if (Roo.isGecko && !hasAbsolute) {
1927             var dbd = fly(bd);
1928             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1929             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1930         }
1931
1932         p = el.parentNode;
1933         while (p && p != bd) {
1934             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1935                 x -= p.scrollLeft;
1936                 y -= p.scrollTop;
1937             }
1938             p = p.parentNode;
1939         }
1940         return [x, y];
1941     },
1942  
1943   
1944
1945
1946     setXY : function(el, xy) {
1947         el = Roo.fly(el, '_setXY');
1948         el.position();
1949         var pts = el.translatePoints(xy);
1950         if (xy[0] !== false) {
1951             el.dom.style.left = pts.left + "px";
1952         }
1953         if (xy[1] !== false) {
1954             el.dom.style.top = pts.top + "px";
1955         }
1956     },
1957
1958     setX : function(el, x) {
1959         this.setXY(el, [x, false]);
1960     },
1961
1962     setY : function(el, y) {
1963         this.setXY(el, [false, y]);
1964     }
1965 };
1966 /*
1967  * Portions of this file are based on pieces of Yahoo User Interface Library
1968  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1969  * YUI licensed under the BSD License:
1970  * http://developer.yahoo.net/yui/license.txt
1971  * <script type="text/javascript">
1972  *
1973  */
1974
1975 Roo.lib.Event = function() {
1976     var loadComplete = false;
1977     var listeners = [];
1978     var unloadListeners = [];
1979     var retryCount = 0;
1980     var onAvailStack = [];
1981     var counter = 0;
1982     var lastError = null;
1983
1984     return {
1985         POLL_RETRYS: 200,
1986         POLL_INTERVAL: 20,
1987         EL: 0,
1988         TYPE: 1,
1989         FN: 2,
1990         WFN: 3,
1991         OBJ: 3,
1992         ADJ_SCOPE: 4,
1993         _interval: null,
1994
1995         startInterval: function() {
1996             if (!this._interval) {
1997                 var self = this;
1998                 var callback = function() {
1999                     self._tryPreloadAttach();
2000                 };
2001                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2002
2003             }
2004         },
2005
2006         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2007             onAvailStack.push({ id:         p_id,
2008                 fn:         p_fn,
2009                 obj:        p_obj,
2010                 override:   p_override,
2011                 checkReady: false    });
2012
2013             retryCount = this.POLL_RETRYS;
2014             this.startInterval();
2015         },
2016
2017
2018         addListener: function(el, eventName, fn) {
2019             el = Roo.getDom(el);
2020             if (!el || !fn) {
2021                 return false;
2022             }
2023
2024             if ("unload" == eventName) {
2025                 unloadListeners[unloadListeners.length] =
2026                 [el, eventName, fn];
2027                 return true;
2028             }
2029
2030             var wrappedFn = function(e) {
2031                 return fn(Roo.lib.Event.getEvent(e));
2032             };
2033
2034             var li = [el, eventName, fn, wrappedFn];
2035
2036             var index = listeners.length;
2037             listeners[index] = li;
2038
2039             this.doAdd(el, eventName, wrappedFn, false);
2040             return true;
2041
2042         },
2043
2044
2045         removeListener: function(el, eventName, fn) {
2046             var i, len;
2047
2048             el = Roo.getDom(el);
2049
2050             if(!fn) {
2051                 return this.purgeElement(el, false, eventName);
2052             }
2053
2054
2055             if ("unload" == eventName) {
2056
2057                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2058                     var li = unloadListeners[i];
2059                     if (li &&
2060                         li[0] == el &&
2061                         li[1] == eventName &&
2062                         li[2] == fn) {
2063                         unloadListeners.splice(i, 1);
2064                         return true;
2065                     }
2066                 }
2067
2068                 return false;
2069             }
2070
2071             var cacheItem = null;
2072
2073
2074             var index = arguments[3];
2075
2076             if ("undefined" == typeof index) {
2077                 index = this._getCacheIndex(el, eventName, fn);
2078             }
2079
2080             if (index >= 0) {
2081                 cacheItem = listeners[index];
2082             }
2083
2084             if (!el || !cacheItem) {
2085                 return false;
2086             }
2087
2088             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2089
2090             delete listeners[index][this.WFN];
2091             delete listeners[index][this.FN];
2092             listeners.splice(index, 1);
2093
2094             return true;
2095
2096         },
2097
2098
2099         getTarget: function(ev, resolveTextNode) {
2100             ev = ev.browserEvent || ev;
2101             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2102             var t = ev.target || ev.srcElement;
2103             return this.resolveTextNode(t);
2104         },
2105
2106
2107         resolveTextNode: function(node) {
2108             if (Roo.isSafari && node && 3 == node.nodeType) {
2109                 return node.parentNode;
2110             } else {
2111                 return node;
2112             }
2113         },
2114
2115
2116         getPageX: function(ev) {
2117             ev = ev.browserEvent || ev;
2118             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2119             var x = ev.pageX;
2120             if (!x && 0 !== x) {
2121                 x = ev.clientX || 0;
2122
2123                 if (Roo.isIE) {
2124                     x += this.getScroll()[1];
2125                 }
2126             }
2127
2128             return x;
2129         },
2130
2131
2132         getPageY: function(ev) {
2133             ev = ev.browserEvent || ev;
2134             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2135             var y = ev.pageY;
2136             if (!y && 0 !== y) {
2137                 y = ev.clientY || 0;
2138
2139                 if (Roo.isIE) {
2140                     y += this.getScroll()[0];
2141                 }
2142             }
2143
2144
2145             return y;
2146         },
2147
2148
2149         getXY: function(ev) {
2150             ev = ev.browserEvent || ev;
2151             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2152             return [this.getPageX(ev), this.getPageY(ev)];
2153         },
2154
2155
2156         getRelatedTarget: function(ev) {
2157             ev = ev.browserEvent || ev;
2158             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2159             var t = ev.relatedTarget;
2160             if (!t) {
2161                 if (ev.type == "mouseout") {
2162                     t = ev.toElement;
2163                 } else if (ev.type == "mouseover") {
2164                     t = ev.fromElement;
2165                 }
2166             }
2167
2168             return this.resolveTextNode(t);
2169         },
2170
2171
2172         getTime: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             if (!ev.time) {
2176                 var t = new Date().getTime();
2177                 try {
2178                     ev.time = t;
2179                 } catch(ex) {
2180                     this.lastError = ex;
2181                     return t;
2182                 }
2183             }
2184
2185             return ev.time;
2186         },
2187
2188
2189         stopEvent: function(ev) {
2190             this.stopPropagation(ev);
2191             this.preventDefault(ev);
2192         },
2193
2194
2195         stopPropagation: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             if (ev.stopPropagation) {
2198                 ev.stopPropagation();
2199             } else {
2200                 ev.cancelBubble = true;
2201             }
2202         },
2203
2204
2205         preventDefault: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if(ev.preventDefault) {
2208                 ev.preventDefault();
2209             } else {
2210                 ev.returnValue = false;
2211             }
2212         },
2213
2214
2215         getEvent: function(e) {
2216             var ev = e || window.event;
2217             if (!ev) {
2218                 var c = this.getEvent.caller;
2219                 while (c) {
2220                     ev = c.arguments[0];
2221                     if (ev && Event == ev.constructor) {
2222                         break;
2223                     }
2224                     c = c.caller;
2225                 }
2226             }
2227             return ev;
2228         },
2229
2230
2231         getCharCode: function(ev) {
2232             ev = ev.browserEvent || ev;
2233             return ev.charCode || ev.keyCode || 0;
2234         },
2235
2236
2237         _getCacheIndex: function(el, eventName, fn) {
2238             for (var i = 0,len = listeners.length; i < len; ++i) {
2239                 var li = listeners[i];
2240                 if (li &&
2241                     li[this.FN] == fn &&
2242                     li[this.EL] == el &&
2243                     li[this.TYPE] == eventName) {
2244                     return i;
2245                 }
2246             }
2247
2248             return -1;
2249         },
2250
2251
2252         elCache: {},
2253
2254
2255         getEl: function(id) {
2256             return document.getElementById(id);
2257         },
2258
2259
2260         clearCache: function() {
2261         },
2262
2263
2264         _load: function(e) {
2265             loadComplete = true;
2266             var EU = Roo.lib.Event;
2267
2268
2269             if (Roo.isIE) {
2270                 EU.doRemove(window, "load", EU._load);
2271             }
2272         },
2273
2274
2275         _tryPreloadAttach: function() {
2276
2277             if (this.locked) {
2278                 return false;
2279             }
2280
2281             this.locked = true;
2282
2283
2284             var tryAgain = !loadComplete;
2285             if (!tryAgain) {
2286                 tryAgain = (retryCount > 0);
2287             }
2288
2289
2290             var notAvail = [];
2291             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2292                 var item = onAvailStack[i];
2293                 if (item) {
2294                     var el = this.getEl(item.id);
2295
2296                     if (el) {
2297                         if (!item.checkReady ||
2298                             loadComplete ||
2299                             el.nextSibling ||
2300                             (document && document.body)) {
2301
2302                             var scope = el;
2303                             if (item.override) {
2304                                 if (item.override === true) {
2305                                     scope = item.obj;
2306                                 } else {
2307                                     scope = item.override;
2308                                 }
2309                             }
2310                             item.fn.call(scope, item.obj);
2311                             onAvailStack[i] = null;
2312                         }
2313                     } else {
2314                         notAvail.push(item);
2315                     }
2316                 }
2317             }
2318
2319             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2320
2321             if (tryAgain) {
2322
2323                 this.startInterval();
2324             } else {
2325                 clearInterval(this._interval);
2326                 this._interval = null;
2327             }
2328
2329             this.locked = false;
2330
2331             return true;
2332
2333         },
2334
2335
2336         purgeElement: function(el, recurse, eventName) {
2337             var elListeners = this.getListeners(el, eventName);
2338             if (elListeners) {
2339                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2340                     var l = elListeners[i];
2341                     this.removeListener(el, l.type, l.fn);
2342                 }
2343             }
2344
2345             if (recurse && el && el.childNodes) {
2346                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2347                     this.purgeElement(el.childNodes[i], recurse, eventName);
2348                 }
2349             }
2350         },
2351
2352
2353         getListeners: function(el, eventName) {
2354             var results = [], searchLists;
2355             if (!eventName) {
2356                 searchLists = [listeners, unloadListeners];
2357             } else if (eventName == "unload") {
2358                 searchLists = [unloadListeners];
2359             } else {
2360                 searchLists = [listeners];
2361             }
2362
2363             for (var j = 0; j < searchLists.length; ++j) {
2364                 var searchList = searchLists[j];
2365                 if (searchList && searchList.length > 0) {
2366                     for (var i = 0,len = searchList.length; i < len; ++i) {
2367                         var l = searchList[i];
2368                         if (l && l[this.EL] === el &&
2369                             (!eventName || eventName === l[this.TYPE])) {
2370                             results.push({
2371                                 type:   l[this.TYPE],
2372                                 fn:     l[this.FN],
2373                                 obj:    l[this.OBJ],
2374                                 adjust: l[this.ADJ_SCOPE],
2375                                 index:  i
2376                             });
2377                         }
2378                     }
2379                 }
2380             }
2381
2382             return (results.length) ? results : null;
2383         },
2384
2385
2386         _unload: function(e) {
2387
2388             var EU = Roo.lib.Event, i, j, l, len, index;
2389
2390             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2391                 l = unloadListeners[i];
2392                 if (l) {
2393                     var scope = window;
2394                     if (l[EU.ADJ_SCOPE]) {
2395                         if (l[EU.ADJ_SCOPE] === true) {
2396                             scope = l[EU.OBJ];
2397                         } else {
2398                             scope = l[EU.ADJ_SCOPE];
2399                         }
2400                     }
2401                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2402                     unloadListeners[i] = null;
2403                     l = null;
2404                     scope = null;
2405                 }
2406             }
2407
2408             unloadListeners = null;
2409
2410             if (listeners && listeners.length > 0) {
2411                 j = listeners.length;
2412                 while (j) {
2413                     index = j - 1;
2414                     l = listeners[index];
2415                     if (l) {
2416                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2417                                 l[EU.FN], index);
2418                     }
2419                     j = j - 1;
2420                 }
2421                 l = null;
2422
2423                 EU.clearCache();
2424             }
2425
2426             EU.doRemove(window, "unload", EU._unload);
2427
2428         },
2429
2430
2431         getScroll: function() {
2432             var dd = document.documentElement, db = document.body;
2433             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2434                 return [dd.scrollTop, dd.scrollLeft];
2435             } else if (db) {
2436                 return [db.scrollTop, db.scrollLeft];
2437             } else {
2438                 return [0, 0];
2439             }
2440         },
2441
2442
2443         doAdd: function () {
2444             if (window.addEventListener) {
2445                 return function(el, eventName, fn, capture) {
2446                     el.addEventListener(eventName, fn, (capture));
2447                 };
2448             } else if (window.attachEvent) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.attachEvent("on" + eventName, fn);
2451                 };
2452             } else {
2453                 return function() {
2454                 };
2455             }
2456         }(),
2457
2458
2459         doRemove: function() {
2460             if (window.removeEventListener) {
2461                 return function (el, eventName, fn, capture) {
2462                     el.removeEventListener(eventName, fn, (capture));
2463                 };
2464             } else if (window.detachEvent) {
2465                 return function (el, eventName, fn) {
2466                     el.detachEvent("on" + eventName, fn);
2467                 };
2468             } else {
2469                 return function() {
2470                 };
2471             }
2472         }()
2473     };
2474     
2475 }();
2476 (function() {     
2477    
2478     var E = Roo.lib.Event;
2479     E.on = E.addListener;
2480     E.un = E.removeListener;
2481
2482     if (document && document.body) {
2483         E._load();
2484     } else {
2485         E.doAdd(window, "load", E._load);
2486     }
2487     E.doAdd(window, "unload", E._unload);
2488     E._tryPreloadAttach();
2489 })();
2490
2491 /*
2492  * Portions of this file are based on pieces of Yahoo User Interface Library
2493  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2494  * YUI licensed under the BSD License:
2495  * http://developer.yahoo.net/yui/license.txt
2496  * <script type="text/javascript">
2497  *
2498  */
2499
2500 (function() {
2501     /**
2502      * @class Roo.lib.Ajax
2503      *
2504      */
2505     Roo.lib.Ajax = {
2506         /**
2507          * @static 
2508          */
2509         request : function(method, uri, cb, data, options) {
2510             if(options){
2511                 var hs = options.headers;
2512                 if(hs){
2513                     for(var h in hs){
2514                         if(hs.hasOwnProperty(h)){
2515                             this.initHeader(h, hs[h], false);
2516                         }
2517                     }
2518                 }
2519                 if(options.xmlData){
2520                     this.initHeader('Content-Type', 'text/xml', false);
2521                     method = 'POST';
2522                     data = options.xmlData;
2523                 }
2524             }
2525
2526             return this.asyncRequest(method, uri, cb, data);
2527         },
2528
2529         serializeForm : function(form) {
2530             if(typeof form == 'string') {
2531                 form = (document.getElementById(form) || document.forms[form]);
2532             }
2533
2534             var el, name, val, disabled, data = '', hasSubmit = false;
2535             for (var i = 0; i < form.elements.length; i++) {
2536                 el = form.elements[i];
2537                 disabled = form.elements[i].disabled;
2538                 name = form.elements[i].name;
2539                 val = form.elements[i].value;
2540
2541                 if (!disabled && name){
2542                     switch (el.type)
2543                             {
2544                         case 'select-one':
2545                         case 'select-multiple':
2546                             for (var j = 0; j < el.options.length; j++) {
2547                                 if (el.options[j].selected) {
2548                                     if (Roo.isIE) {
2549                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2550                                     }
2551                                     else {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                 }
2555                             }
2556                             break;
2557                         case 'radio':
2558                         case 'checkbox':
2559                             if (el.checked) {
2560                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2561                             }
2562                             break;
2563                         case 'file':
2564
2565                         case undefined:
2566
2567                         case 'reset':
2568
2569                         case 'button':
2570
2571                             break;
2572                         case 'submit':
2573                             if(hasSubmit == false) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                                 hasSubmit = true;
2576                             }
2577                             break;
2578                         default:
2579                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2580                             break;
2581                     }
2582                 }
2583             }
2584             data = data.substr(0, data.length - 1);
2585             return data;
2586         },
2587
2588         headers:{},
2589
2590         hasHeaders:false,
2591
2592         useDefaultHeader:true,
2593
2594         defaultPostHeader:'application/x-www-form-urlencoded',
2595
2596         useDefaultXhrHeader:true,
2597
2598         defaultXhrHeader:'XMLHttpRequest',
2599
2600         hasDefaultHeaders:true,
2601
2602         defaultHeaders:{},
2603
2604         poll:{},
2605
2606         timeout:{},
2607
2608         pollInterval:50,
2609
2610         transactionId:0,
2611
2612         setProgId:function(id)
2613         {
2614             this.activeX.unshift(id);
2615         },
2616
2617         setDefaultPostHeader:function(b)
2618         {
2619             this.useDefaultHeader = b;
2620         },
2621
2622         setDefaultXhrHeader:function(b)
2623         {
2624             this.useDefaultXhrHeader = b;
2625         },
2626
2627         setPollingInterval:function(i)
2628         {
2629             if (typeof i == 'number' && isFinite(i)) {
2630                 this.pollInterval = i;
2631             }
2632         },
2633
2634         createXhrObject:function(transactionId)
2635         {
2636             var obj,http;
2637             try
2638             {
2639
2640                 http = new XMLHttpRequest();
2641
2642                 obj = { conn:http, tId:transactionId };
2643             }
2644             catch(e)
2645             {
2646                 for (var i = 0; i < this.activeX.length; ++i) {
2647                     try
2648                     {
2649
2650                         http = new ActiveXObject(this.activeX[i]);
2651
2652                         obj = { conn:http, tId:transactionId };
2653                         break;
2654                     }
2655                     catch(e) {
2656                     }
2657                 }
2658             }
2659             finally
2660             {
2661                 return obj;
2662             }
2663         },
2664
2665         getConnectionObject:function()
2666         {
2667             var o;
2668             var tId = this.transactionId;
2669
2670             try
2671             {
2672                 o = this.createXhrObject(tId);
2673                 if (o) {
2674                     this.transactionId++;
2675                 }
2676             }
2677             catch(e) {
2678             }
2679             finally
2680             {
2681                 return o;
2682             }
2683         },
2684
2685         asyncRequest:function(method, uri, callback, postData)
2686         {
2687             var o = this.getConnectionObject();
2688
2689             if (!o) {
2690                 return null;
2691             }
2692             else {
2693                 o.conn.open(method, uri, true);
2694
2695                 if (this.useDefaultXhrHeader) {
2696                     if (!this.defaultHeaders['X-Requested-With']) {
2697                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2698                     }
2699                 }
2700
2701                 if(postData && this.useDefaultHeader){
2702                     this.initHeader('Content-Type', this.defaultPostHeader);
2703                 }
2704
2705                  if (this.hasDefaultHeaders || this.hasHeaders) {
2706                     this.setHeader(o);
2707                 }
2708
2709                 this.handleReadyState(o, callback);
2710                 o.conn.send(postData || null);
2711
2712                 return o;
2713             }
2714         },
2715
2716         handleReadyState:function(o, callback)
2717         {
2718             var oConn = this;
2719
2720             if (callback && callback.timeout) {
2721                 
2722                 this.timeout[o.tId] = window.setTimeout(function() {
2723                     oConn.abort(o, callback, true);
2724                 }, callback.timeout);
2725             }
2726
2727             this.poll[o.tId] = window.setInterval(
2728                     function() {
2729                         if (o.conn && o.conn.readyState == 4) {
2730                             window.clearInterval(oConn.poll[o.tId]);
2731                             delete oConn.poll[o.tId];
2732
2733                             if(callback && callback.timeout) {
2734                                 window.clearTimeout(oConn.timeout[o.tId]);
2735                                 delete oConn.timeout[o.tId];
2736                             }
2737
2738                             oConn.handleTransactionResponse(o, callback);
2739                         }
2740                     }
2741                     , this.pollInterval);
2742         },
2743
2744         handleTransactionResponse:function(o, callback, isAbort)
2745         {
2746
2747             if (!callback) {
2748                 this.releaseObject(o);
2749                 return;
2750             }
2751
2752             var httpStatus, responseObject;
2753
2754             try
2755             {
2756                 if (o.conn.status !== undefined && o.conn.status != 0) {
2757                     httpStatus = o.conn.status;
2758                 }
2759                 else {
2760                     httpStatus = 13030;
2761                 }
2762             }
2763             catch(e) {
2764
2765
2766                 httpStatus = 13030;
2767             }
2768
2769             if (httpStatus >= 200 && httpStatus < 300) {
2770                 responseObject = this.createResponseObject(o, callback.argument);
2771                 if (callback.success) {
2772                     if (!callback.scope) {
2773                         callback.success(responseObject);
2774                     }
2775                     else {
2776
2777
2778                         callback.success.apply(callback.scope, [responseObject]);
2779                     }
2780                 }
2781             }
2782             else {
2783                 switch (httpStatus) {
2784
2785                     case 12002:
2786                     case 12029:
2787                     case 12030:
2788                     case 12031:
2789                     case 12152:
2790                     case 13030:
2791                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2792                         if (callback.failure) {
2793                             if (!callback.scope) {
2794                                 callback.failure(responseObject);
2795                             }
2796                             else {
2797                                 callback.failure.apply(callback.scope, [responseObject]);
2798                             }
2799                         }
2800                         break;
2801                     default:
2802                         responseObject = this.createResponseObject(o, callback.argument);
2803                         if (callback.failure) {
2804                             if (!callback.scope) {
2805                                 callback.failure(responseObject);
2806                             }
2807                             else {
2808                                 callback.failure.apply(callback.scope, [responseObject]);
2809                             }
2810                         }
2811                 }
2812             }
2813
2814             this.releaseObject(o);
2815             responseObject = null;
2816         },
2817
2818         createResponseObject:function(o, callbackArg)
2819         {
2820             var obj = {};
2821             var headerObj = {};
2822
2823             try
2824             {
2825                 var headerStr = o.conn.getAllResponseHeaders();
2826                 var header = headerStr.split('\n');
2827                 for (var i = 0; i < header.length; i++) {
2828                     var delimitPos = header[i].indexOf(':');
2829                     if (delimitPos != -1) {
2830                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2831                     }
2832                 }
2833             }
2834             catch(e) {
2835             }
2836
2837             obj.tId = o.tId;
2838             obj.status = o.conn.status;
2839             obj.statusText = o.conn.statusText;
2840             obj.getResponseHeader = headerObj;
2841             obj.getAllResponseHeaders = headerStr;
2842             obj.responseText = o.conn.responseText;
2843             obj.responseXML = o.conn.responseXML;
2844
2845             if (typeof callbackArg !== undefined) {
2846                 obj.argument = callbackArg;
2847             }
2848
2849             return obj;
2850         },
2851
2852         createExceptionObject:function(tId, callbackArg, isAbort)
2853         {
2854             var COMM_CODE = 0;
2855             var COMM_ERROR = 'communication failure';
2856             var ABORT_CODE = -1;
2857             var ABORT_ERROR = 'transaction aborted';
2858
2859             var obj = {};
2860
2861             obj.tId = tId;
2862             if (isAbort) {
2863                 obj.status = ABORT_CODE;
2864                 obj.statusText = ABORT_ERROR;
2865             }
2866             else {
2867                 obj.status = COMM_CODE;
2868                 obj.statusText = COMM_ERROR;
2869             }
2870
2871             if (callbackArg) {
2872                 obj.argument = callbackArg;
2873             }
2874
2875             return obj;
2876         },
2877
2878         initHeader:function(label, value, isDefault)
2879         {
2880             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2881
2882             if (headerObj[label] === undefined) {
2883                 headerObj[label] = value;
2884             }
2885             else {
2886
2887
2888                 headerObj[label] = value + "," + headerObj[label];
2889             }
2890
2891             if (isDefault) {
2892                 this.hasDefaultHeaders = true;
2893             }
2894             else {
2895                 this.hasHeaders = true;
2896             }
2897         },
2898
2899
2900         setHeader:function(o)
2901         {
2902             if (this.hasDefaultHeaders) {
2903                 for (var prop in this.defaultHeaders) {
2904                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2905                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2906                     }
2907                 }
2908             }
2909
2910             if (this.hasHeaders) {
2911                 for (var prop in this.headers) {
2912                     if (this.headers.hasOwnProperty(prop)) {
2913                         o.conn.setRequestHeader(prop, this.headers[prop]);
2914                     }
2915                 }
2916                 this.headers = {};
2917                 this.hasHeaders = false;
2918             }
2919         },
2920
2921         resetDefaultHeaders:function() {
2922             delete this.defaultHeaders;
2923             this.defaultHeaders = {};
2924             this.hasDefaultHeaders = false;
2925         },
2926
2927         abort:function(o, callback, isTimeout)
2928         {
2929             if(this.isCallInProgress(o)) {
2930                 o.conn.abort();
2931                 window.clearInterval(this.poll[o.tId]);
2932                 delete this.poll[o.tId];
2933                 if (isTimeout) {
2934                     delete this.timeout[o.tId];
2935                 }
2936
2937                 this.handleTransactionResponse(o, callback, true);
2938
2939                 return true;
2940             }
2941             else {
2942                 return false;
2943             }
2944         },
2945
2946
2947         isCallInProgress:function(o)
2948         {
2949             if (o && o.conn) {
2950                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2951             }
2952             else {
2953
2954                 return false;
2955             }
2956         },
2957
2958
2959         releaseObject:function(o)
2960         {
2961
2962             o.conn = null;
2963
2964             o = null;
2965         },
2966
2967         activeX:[
2968         'MSXML2.XMLHTTP.3.0',
2969         'MSXML2.XMLHTTP',
2970         'Microsoft.XMLHTTP'
2971         ]
2972
2973
2974     };
2975 })();/*
2976  * Portions of this file are based on pieces of Yahoo User Interface Library
2977  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2978  * YUI licensed under the BSD License:
2979  * http://developer.yahoo.net/yui/license.txt
2980  * <script type="text/javascript">
2981  *
2982  */
2983
2984 Roo.lib.Region = function(t, r, b, l) {
2985     this.top = t;
2986     this[1] = t;
2987     this.right = r;
2988     this.bottom = b;
2989     this.left = l;
2990     this[0] = l;
2991 };
2992
2993
2994 Roo.lib.Region.prototype = {
2995     contains : function(region) {
2996         return ( region.left >= this.left &&
2997                  region.right <= this.right &&
2998                  region.top >= this.top &&
2999                  region.bottom <= this.bottom    );
3000
3001     },
3002
3003     getArea : function() {
3004         return ( (this.bottom - this.top) * (this.right - this.left) );
3005     },
3006
3007     intersect : function(region) {
3008         var t = Math.max(this.top, region.top);
3009         var r = Math.min(this.right, region.right);
3010         var b = Math.min(this.bottom, region.bottom);
3011         var l = Math.max(this.left, region.left);
3012
3013         if (b >= t && r >= l) {
3014             return new Roo.lib.Region(t, r, b, l);
3015         } else {
3016             return null;
3017         }
3018     },
3019     union : function(region) {
3020         var t = Math.min(this.top, region.top);
3021         var r = Math.max(this.right, region.right);
3022         var b = Math.max(this.bottom, region.bottom);
3023         var l = Math.min(this.left, region.left);
3024
3025         return new Roo.lib.Region(t, r, b, l);
3026     },
3027
3028     adjust : function(t, l, b, r) {
3029         this.top += t;
3030         this.left += l;
3031         this.right += r;
3032         this.bottom += b;
3033         return this;
3034     }
3035 };
3036
3037 Roo.lib.Region.getRegion = function(el) {
3038     var p = Roo.lib.Dom.getXY(el);
3039
3040     var t = p[1];
3041     var r = p[0] + el.offsetWidth;
3042     var b = p[1] + el.offsetHeight;
3043     var l = p[0];
3044
3045     return new Roo.lib.Region(t, r, b, l);
3046 };
3047 /*
3048  * Portions of this file are based on pieces of Yahoo User Interface Library
3049  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3050  * YUI licensed under the BSD License:
3051  * http://developer.yahoo.net/yui/license.txt
3052  * <script type="text/javascript">
3053  *
3054  */
3055 //@@dep Roo.lib.Region
3056
3057
3058 Roo.lib.Point = function(x, y) {
3059     if (x instanceof Array) {
3060         y = x[1];
3061         x = x[0];
3062     }
3063     this.x = this.right = this.left = this[0] = x;
3064     this.y = this.top = this.bottom = this[1] = y;
3065 };
3066
3067 Roo.lib.Point.prototype = new Roo.lib.Region();
3068 /*
3069  * Portions of this file are based on pieces of Yahoo User Interface Library
3070  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3071  * YUI licensed under the BSD License:
3072  * http://developer.yahoo.net/yui/license.txt
3073  * <script type="text/javascript">
3074  *
3075  */
3076  
3077 (function() {   
3078
3079     Roo.lib.Anim = {
3080         scroll : function(el, args, duration, easing, cb, scope) {
3081             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3082         },
3083
3084         motion : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3086         },
3087
3088         color : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3090         },
3091
3092         run : function(el, args, duration, easing, cb, scope, type) {
3093             type = type || Roo.lib.AnimBase;
3094             if (typeof easing == "string") {
3095                 easing = Roo.lib.Easing[easing];
3096             }
3097             var anim = new type(el, args, duration, easing);
3098             anim.animateX(function() {
3099                 Roo.callback(cb, scope);
3100             });
3101             return anim;
3102         }
3103     };
3104 })();/*
3105  * Portions of this file are based on pieces of Yahoo User Interface Library
3106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3107  * YUI licensed under the BSD License:
3108  * http://developer.yahoo.net/yui/license.txt
3109  * <script type="text/javascript">
3110  *
3111  */
3112
3113 (function() {    
3114     var libFlyweight;
3115     
3116     function fly(el) {
3117         if (!libFlyweight) {
3118             libFlyweight = new Roo.Element.Flyweight();
3119         }
3120         libFlyweight.dom = el;
3121         return libFlyweight;
3122     }
3123
3124     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3125     
3126    
3127     
3128     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3129         if (el) {
3130             this.init(el, attributes, duration, method);
3131         }
3132     };
3133
3134     Roo.lib.AnimBase.fly = fly;
3135     
3136     
3137     
3138     Roo.lib.AnimBase.prototype = {
3139
3140         toString: function() {
3141             var el = this.getEl();
3142             var id = el.id || el.tagName;
3143             return ("Anim " + id);
3144         },
3145
3146         patterns: {
3147             noNegatives:        /width|height|opacity|padding/i,
3148             offsetAttribute:  /^((width|height)|(top|left))$/,
3149             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3150             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3151         },
3152
3153
3154         doMethod: function(attr, start, end) {
3155             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3156         },
3157
3158
3159         setAttribute: function(attr, val, unit) {
3160             if (this.patterns.noNegatives.test(attr)) {
3161                 val = (val > 0) ? val : 0;
3162             }
3163
3164             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3165         },
3166
3167
3168         getAttribute: function(attr) {
3169             var el = this.getEl();
3170             var val = fly(el).getStyle(attr);
3171
3172             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3173                 return parseFloat(val);
3174             }
3175
3176             var a = this.patterns.offsetAttribute.exec(attr) || [];
3177             var pos = !!( a[3] );
3178             var box = !!( a[2] );
3179
3180
3181             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3182                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3183             } else {
3184                 val = 0;
3185             }
3186
3187             return val;
3188         },
3189
3190
3191         getDefaultUnit: function(attr) {
3192             if (this.patterns.defaultUnit.test(attr)) {
3193                 return 'px';
3194             }
3195
3196             return '';
3197         },
3198
3199         animateX : function(callback, scope) {
3200             var f = function() {
3201                 this.onComplete.removeListener(f);
3202                 if (typeof callback == "function") {
3203                     callback.call(scope || this, this);
3204                 }
3205             };
3206             this.onComplete.addListener(f, this);
3207             this.animate();
3208         },
3209
3210
3211         setRuntimeAttribute: function(attr) {
3212             var start;
3213             var end;
3214             var attributes = this.attributes;
3215
3216             this.runtimeAttributes[attr] = {};
3217
3218             var isset = function(prop) {
3219                 return (typeof prop !== 'undefined');
3220             };
3221
3222             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3223                 return false;
3224             }
3225
3226             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3227
3228
3229             if (isset(attributes[attr]['to'])) {
3230                 end = attributes[attr]['to'];
3231             } else if (isset(attributes[attr]['by'])) {
3232                 if (start.constructor == Array) {
3233                     end = [];
3234                     for (var i = 0, len = start.length; i < len; ++i) {
3235                         end[i] = start[i] + attributes[attr]['by'][i];
3236                     }
3237                 } else {
3238                     end = start + attributes[attr]['by'];
3239                 }
3240             }
3241
3242             this.runtimeAttributes[attr].start = start;
3243             this.runtimeAttributes[attr].end = end;
3244
3245
3246             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3247         },
3248
3249
3250         init: function(el, attributes, duration, method) {
3251
3252             var isAnimated = false;
3253
3254
3255             var startTime = null;
3256
3257
3258             var actualFrames = 0;
3259
3260
3261             el = Roo.getDom(el);
3262
3263
3264             this.attributes = attributes || {};
3265
3266
3267             this.duration = duration || 1;
3268
3269
3270             this.method = method || Roo.lib.Easing.easeNone;
3271
3272
3273             this.useSeconds = true;
3274
3275
3276             this.currentFrame = 0;
3277
3278
3279             this.totalFrames = Roo.lib.AnimMgr.fps;
3280
3281
3282             this.getEl = function() {
3283                 return el;
3284             };
3285
3286
3287             this.isAnimated = function() {
3288                 return isAnimated;
3289             };
3290
3291
3292             this.getStartTime = function() {
3293                 return startTime;
3294             };
3295
3296             this.runtimeAttributes = {};
3297
3298
3299             this.animate = function() {
3300                 if (this.isAnimated()) {
3301                     return false;
3302                 }
3303
3304                 this.currentFrame = 0;
3305
3306                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3307
3308                 Roo.lib.AnimMgr.registerElement(this);
3309             };
3310
3311
3312             this.stop = function(finish) {
3313                 if (finish) {
3314                     this.currentFrame = this.totalFrames;
3315                     this._onTween.fire();
3316                 }
3317                 Roo.lib.AnimMgr.stop(this);
3318             };
3319
3320             var onStart = function() {
3321                 this.onStart.fire();
3322
3323                 this.runtimeAttributes = {};
3324                 for (var attr in this.attributes) {
3325                     this.setRuntimeAttribute(attr);
3326                 }
3327
3328                 isAnimated = true;
3329                 actualFrames = 0;
3330                 startTime = new Date();
3331             };
3332
3333
3334             var onTween = function() {
3335                 var data = {
3336                     duration: new Date() - this.getStartTime(),
3337                     currentFrame: this.currentFrame
3338                 };
3339
3340                 data.toString = function() {
3341                     return (
3342                             'duration: ' + data.duration +
3343                             ', currentFrame: ' + data.currentFrame
3344                             );
3345                 };
3346
3347                 this.onTween.fire(data);
3348
3349                 var runtimeAttributes = this.runtimeAttributes;
3350
3351                 for (var attr in runtimeAttributes) {
3352                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3353                 }
3354
3355                 actualFrames += 1;
3356             };
3357
3358             var onComplete = function() {
3359                 var actual_duration = (new Date() - startTime) / 1000 ;
3360
3361                 var data = {
3362                     duration: actual_duration,
3363                     frames: actualFrames,
3364                     fps: actualFrames / actual_duration
3365                 };
3366
3367                 data.toString = function() {
3368                     return (
3369                             'duration: ' + data.duration +
3370                             ', frames: ' + data.frames +
3371                             ', fps: ' + data.fps
3372                             );
3373                 };
3374
3375                 isAnimated = false;
3376                 actualFrames = 0;
3377                 this.onComplete.fire(data);
3378             };
3379
3380
3381             this._onStart = new Roo.util.Event(this);
3382             this.onStart = new Roo.util.Event(this);
3383             this.onTween = new Roo.util.Event(this);
3384             this._onTween = new Roo.util.Event(this);
3385             this.onComplete = new Roo.util.Event(this);
3386             this._onComplete = new Roo.util.Event(this);
3387             this._onStart.addListener(onStart);
3388             this._onTween.addListener(onTween);
3389             this._onComplete.addListener(onComplete);
3390         }
3391     };
3392 })();
3393 /*
3394  * Portions of this file are based on pieces of Yahoo User Interface Library
3395  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3396  * YUI licensed under the BSD License:
3397  * http://developer.yahoo.net/yui/license.txt
3398  * <script type="text/javascript">
3399  *
3400  */
3401
3402 Roo.lib.AnimMgr = new function() {
3403
3404     var thread = null;
3405
3406
3407     var queue = [];
3408
3409
3410     var tweenCount = 0;
3411
3412
3413     this.fps = 1000;
3414
3415
3416     this.delay = 1;
3417
3418
3419     this.registerElement = function(tween) {
3420         queue[queue.length] = tween;
3421         tweenCount += 1;
3422         tween._onStart.fire();
3423         this.start();
3424     };
3425
3426
3427     this.unRegister = function(tween, index) {
3428         tween._onComplete.fire();
3429         index = index || getIndex(tween);
3430         if (index != -1) {
3431             queue.splice(index, 1);
3432         }
3433
3434         tweenCount -= 1;
3435         if (tweenCount <= 0) {
3436             this.stop();
3437         }
3438     };
3439
3440
3441     this.start = function() {
3442         if (thread === null) {
3443             thread = setInterval(this.run, this.delay);
3444         }
3445     };
3446
3447
3448     this.stop = function(tween) {
3449         if (!tween) {
3450             clearInterval(thread);
3451
3452             for (var i = 0, len = queue.length; i < len; ++i) {
3453                 if (queue[0].isAnimated()) {
3454                     this.unRegister(queue[0], 0);
3455                 }
3456             }
3457
3458             queue = [];
3459             thread = null;
3460             tweenCount = 0;
3461         }
3462         else {
3463             this.unRegister(tween);
3464         }
3465     };
3466
3467
3468     this.run = function() {
3469         for (var i = 0, len = queue.length; i < len; ++i) {
3470             var tween = queue[i];
3471             if (!tween || !tween.isAnimated()) {
3472                 continue;
3473             }
3474
3475             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3476             {
3477                 tween.currentFrame += 1;
3478
3479                 if (tween.useSeconds) {
3480                     correctFrame(tween);
3481                 }
3482                 tween._onTween.fire();
3483             }
3484             else {
3485                 Roo.lib.AnimMgr.stop(tween, i);
3486             }
3487         }
3488     };
3489
3490     var getIndex = function(anim) {
3491         for (var i = 0, len = queue.length; i < len; ++i) {
3492             if (queue[i] == anim) {
3493                 return i;
3494             }
3495         }
3496         return -1;
3497     };
3498
3499
3500     var correctFrame = function(tween) {
3501         var frames = tween.totalFrames;
3502         var frame = tween.currentFrame;
3503         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3504         var elapsed = (new Date() - tween.getStartTime());
3505         var tweak = 0;
3506
3507         if (elapsed < tween.duration * 1000) {
3508             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3509         } else {
3510             tweak = frames - (frame + 1);
3511         }
3512         if (tweak > 0 && isFinite(tweak)) {
3513             if (tween.currentFrame + tweak >= frames) {
3514                 tweak = frames - (frame + 1);
3515             }
3516
3517             tween.currentFrame += tweak;
3518         }
3519     };
3520 };
3521
3522     /*
3523  * Portions of this file are based on pieces of Yahoo User Interface Library
3524  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3525  * YUI licensed under the BSD License:
3526  * http://developer.yahoo.net/yui/license.txt
3527  * <script type="text/javascript">
3528  *
3529  */
3530 Roo.lib.Bezier = new function() {
3531
3532         this.getPosition = function(points, t) {
3533             var n = points.length;
3534             var tmp = [];
3535
3536             for (var i = 0; i < n; ++i) {
3537                 tmp[i] = [points[i][0], points[i][1]];
3538             }
3539
3540             for (var j = 1; j < n; ++j) {
3541                 for (i = 0; i < n - j; ++i) {
3542                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3543                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3544                 }
3545             }
3546
3547             return [ tmp[0][0], tmp[0][1] ];
3548
3549         };
3550     };/*
3551  * Portions of this file are based on pieces of Yahoo User Interface Library
3552  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3553  * YUI licensed under the BSD License:
3554  * http://developer.yahoo.net/yui/license.txt
3555  * <script type="text/javascript">
3556  *
3557  */
3558 (function() {
3559
3560     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3561         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3562     };
3563
3564     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3565
3566     var fly = Roo.lib.AnimBase.fly;
3567     var Y = Roo.lib;
3568     var superclass = Y.ColorAnim.superclass;
3569     var proto = Y.ColorAnim.prototype;
3570
3571     proto.toString = function() {
3572         var el = this.getEl();
3573         var id = el.id || el.tagName;
3574         return ("ColorAnim " + id);
3575     };
3576
3577     proto.patterns.color = /color$/i;
3578     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3579     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3580     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3581     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3582
3583
3584     proto.parseColor = function(s) {
3585         if (s.length == 3) {
3586             return s;
3587         }
3588
3589         var c = this.patterns.hex.exec(s);
3590         if (c && c.length == 4) {
3591             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3592         }
3593
3594         c = this.patterns.rgb.exec(s);
3595         if (c && c.length == 4) {
3596             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3597         }
3598
3599         c = this.patterns.hex3.exec(s);
3600         if (c && c.length == 4) {
3601             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3602         }
3603
3604         return null;
3605     };
3606     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3607     proto.getAttribute = function(attr) {
3608         var el = this.getEl();
3609         if (this.patterns.color.test(attr)) {
3610             var val = fly(el).getStyle(attr);
3611
3612             if (this.patterns.transparent.test(val)) {
3613                 var parent = el.parentNode;
3614                 val = fly(parent).getStyle(attr);
3615
3616                 while (parent && this.patterns.transparent.test(val)) {
3617                     parent = parent.parentNode;
3618                     val = fly(parent).getStyle(attr);
3619                     if (parent.tagName.toUpperCase() == 'HTML') {
3620                         val = '#fff';
3621                     }
3622                 }
3623             }
3624         } else {
3625             val = superclass.getAttribute.call(this, attr);
3626         }
3627
3628         return val;
3629     };
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653
3654     proto.doMethod = function(attr, start, end) {
3655         var val;
3656
3657         if (this.patterns.color.test(attr)) {
3658             val = [];
3659             for (var i = 0, len = start.length; i < len; ++i) {
3660                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3661             }
3662
3663             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3664         }
3665         else {
3666             val = superclass.doMethod.call(this, attr, start, end);
3667         }
3668
3669         return val;
3670     };
3671
3672     proto.setRuntimeAttribute = function(attr) {
3673         superclass.setRuntimeAttribute.call(this, attr);
3674
3675         if (this.patterns.color.test(attr)) {
3676             var attributes = this.attributes;
3677             var start = this.parseColor(this.runtimeAttributes[attr].start);
3678             var end = this.parseColor(this.runtimeAttributes[attr].end);
3679
3680             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3681                 end = this.parseColor(attributes[attr].by);
3682
3683                 for (var i = 0, len = start.length; i < len; ++i) {
3684                     end[i] = start[i] + end[i];
3685                 }
3686             }
3687
3688             this.runtimeAttributes[attr].start = start;
3689             this.runtimeAttributes[attr].end = end;
3690         }
3691     };
3692 })();
3693
3694 /*
3695  * Portions of this file are based on pieces of Yahoo User Interface Library
3696  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3697  * YUI licensed under the BSD License:
3698  * http://developer.yahoo.net/yui/license.txt
3699  * <script type="text/javascript">
3700  *
3701  */
3702 Roo.lib.Easing = {
3703
3704
3705     easeNone: function (t, b, c, d) {
3706         return c * t / d + b;
3707     },
3708
3709
3710     easeIn: function (t, b, c, d) {
3711         return c * (t /= d) * t + b;
3712     },
3713
3714
3715     easeOut: function (t, b, c, d) {
3716         return -c * (t /= d) * (t - 2) + b;
3717     },
3718
3719
3720     easeBoth: function (t, b, c, d) {
3721         if ((t /= d / 2) < 1) {
3722             return c / 2 * t * t + b;
3723         }
3724
3725         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3726     },
3727
3728
3729     easeInStrong: function (t, b, c, d) {
3730         return c * (t /= d) * t * t * t + b;
3731     },
3732
3733
3734     easeOutStrong: function (t, b, c, d) {
3735         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3736     },
3737
3738
3739     easeBothStrong: function (t, b, c, d) {
3740         if ((t /= d / 2) < 1) {
3741             return c / 2 * t * t * t * t + b;
3742         }
3743
3744         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3745     },
3746
3747
3748
3749     elasticIn: function (t, b, c, d, a, p) {
3750         if (t == 0) {
3751             return b;
3752         }
3753         if ((t /= d) == 1) {
3754             return b + c;
3755         }
3756         if (!p) {
3757             p = d * .3;
3758         }
3759
3760         if (!a || a < Math.abs(c)) {
3761             a = c;
3762             var s = p / 4;
3763         }
3764         else {
3765             var s = p / (2 * Math.PI) * Math.asin(c / a);
3766         }
3767
3768         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3769     },
3770
3771
3772     elasticOut: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3792     },
3793
3794
3795     elasticBoth: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799
3800         if ((t /= d / 2) == 2) {
3801             return b + c;
3802         }
3803
3804         if (!p) {
3805             p = d * (.3 * 1.5);
3806         }
3807
3808         if (!a || a < Math.abs(c)) {
3809             a = c;
3810             var s = p / 4;
3811         }
3812         else {
3813             var s = p / (2 * Math.PI) * Math.asin(c / a);
3814         }
3815
3816         if (t < 1) {
3817             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3818                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3819         }
3820         return a * Math.pow(2, -10 * (t -= 1)) *
3821                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3822     },
3823
3824
3825
3826     backIn: function (t, b, c, d, s) {
3827         if (typeof s == 'undefined') {
3828             s = 1.70158;
3829         }
3830         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3831     },
3832
3833
3834     backOut: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3839     },
3840
3841
3842     backBoth: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846
3847         if ((t /= d / 2 ) < 1) {
3848             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3849         }
3850         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3851     },
3852
3853
3854     bounceIn: function (t, b, c, d) {
3855         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3856     },
3857
3858
3859     bounceOut: function (t, b, c, d) {
3860         if ((t /= d) < (1 / 2.75)) {
3861             return c * (7.5625 * t * t) + b;
3862         } else if (t < (2 / 2.75)) {
3863             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3864         } else if (t < (2.5 / 2.75)) {
3865             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3866         }
3867         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3868     },
3869
3870
3871     bounceBoth: function (t, b, c, d) {
3872         if (t < d / 2) {
3873             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3874         }
3875         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3876     }
3877 };/*
3878  * Portions of this file are based on pieces of Yahoo User Interface Library
3879  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3880  * YUI licensed under the BSD License:
3881  * http://developer.yahoo.net/yui/license.txt
3882  * <script type="text/javascript">
3883  *
3884  */
3885     (function() {
3886         Roo.lib.Motion = function(el, attributes, duration, method) {
3887             if (el) {
3888                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3889             }
3890         };
3891
3892         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3893
3894
3895         var Y = Roo.lib;
3896         var superclass = Y.Motion.superclass;
3897         var proto = Y.Motion.prototype;
3898
3899         proto.toString = function() {
3900             var el = this.getEl();
3901             var id = el.id || el.tagName;
3902             return ("Motion " + id);
3903         };
3904
3905         proto.patterns.points = /^points$/i;
3906
3907         proto.setAttribute = function(attr, val, unit) {
3908             if (this.patterns.points.test(attr)) {
3909                 unit = unit || 'px';
3910                 superclass.setAttribute.call(this, 'left', val[0], unit);
3911                 superclass.setAttribute.call(this, 'top', val[1], unit);
3912             } else {
3913                 superclass.setAttribute.call(this, attr, val, unit);
3914             }
3915         };
3916
3917         proto.getAttribute = function(attr) {
3918             if (this.patterns.points.test(attr)) {
3919                 var val = [
3920                         superclass.getAttribute.call(this, 'left'),
3921                         superclass.getAttribute.call(this, 'top')
3922                         ];
3923             } else {
3924                 val = superclass.getAttribute.call(this, attr);
3925             }
3926
3927             return val;
3928         };
3929
3930         proto.doMethod = function(attr, start, end) {
3931             var val = null;
3932
3933             if (this.patterns.points.test(attr)) {
3934                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3935                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3936             } else {
3937                 val = superclass.doMethod.call(this, attr, start, end);
3938             }
3939             return val;
3940         };
3941
3942         proto.setRuntimeAttribute = function(attr) {
3943             if (this.patterns.points.test(attr)) {
3944                 var el = this.getEl();
3945                 var attributes = this.attributes;
3946                 var start;
3947                 var control = attributes['points']['control'] || [];
3948                 var end;
3949                 var i, len;
3950
3951                 if (control.length > 0 && !(control[0] instanceof Array)) {
3952                     control = [control];
3953                 } else {
3954                     var tmp = [];
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         tmp[i] = control[i];
3957                     }
3958                     control = tmp;
3959                 }
3960
3961                 Roo.fly(el).position();
3962
3963                 if (isset(attributes['points']['from'])) {
3964                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3965                 }
3966                 else {
3967                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3968                 }
3969
3970                 start = this.getAttribute('points');
3971
3972
3973                 if (isset(attributes['points']['to'])) {
3974                     end = translateValues.call(this, attributes['points']['to'], start);
3975
3976                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3977                     for (i = 0,len = control.length; i < len; ++i) {
3978                         control[i] = translateValues.call(this, control[i], start);
3979                     }
3980
3981
3982                 } else if (isset(attributes['points']['by'])) {
3983                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3984
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3987                     }
3988                 }
3989
3990                 this.runtimeAttributes[attr] = [start];
3991
3992                 if (control.length > 0) {
3993                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3994                 }
3995
3996                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3997             }
3998             else {
3999                 superclass.setRuntimeAttribute.call(this, attr);
4000             }
4001         };
4002
4003         var translateValues = function(val, start) {
4004             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4005             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4006
4007             return val;
4008         };
4009
4010         var isset = function(prop) {
4011             return (typeof prop !== 'undefined');
4012         };
4013     })();
4014 /*
4015  * Portions of this file are based on pieces of Yahoo User Interface Library
4016  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4017  * YUI licensed under the BSD License:
4018  * http://developer.yahoo.net/yui/license.txt
4019  * <script type="text/javascript">
4020  *
4021  */
4022     (function() {
4023         Roo.lib.Scroll = function(el, attributes, duration, method) {
4024             if (el) {
4025                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4026             }
4027         };
4028
4029         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4030
4031
4032         var Y = Roo.lib;
4033         var superclass = Y.Scroll.superclass;
4034         var proto = Y.Scroll.prototype;
4035
4036         proto.toString = function() {
4037             var el = this.getEl();
4038             var id = el.id || el.tagName;
4039             return ("Scroll " + id);
4040         };
4041
4042         proto.doMethod = function(attr, start, end) {
4043             var val = null;
4044
4045             if (attr == 'scroll') {
4046                 val = [
4047                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4048                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4049                         ];
4050
4051             } else {
4052                 val = superclass.doMethod.call(this, attr, start, end);
4053             }
4054             return val;
4055         };
4056
4057         proto.getAttribute = function(attr) {
4058             var val = null;
4059             var el = this.getEl();
4060
4061             if (attr == 'scroll') {
4062                 val = [ el.scrollLeft, el.scrollTop ];
4063             } else {
4064                 val = superclass.getAttribute.call(this, attr);
4065             }
4066
4067             return val;
4068         };
4069
4070         proto.setAttribute = function(attr, val, unit) {
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 el.scrollLeft = val[0];
4075                 el.scrollTop = val[1];
4076             } else {
4077                 superclass.setAttribute.call(this, attr, val, unit);
4078             }
4079         };
4080     })();
4081 /*
4082  * Based on:
4083  * Ext JS Library 1.1.1
4084  * Copyright(c) 2006-2007, Ext JS, LLC.
4085  *
4086  * Originally Released Under LGPL - original licence link has changed is not relivant.
4087  *
4088  * Fork - LGPL
4089  * <script type="text/javascript">
4090  */
4091
4092
4093 // nasty IE9 hack - what a pile of crap that is..
4094
4095  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4096     Range.prototype.createContextualFragment = function (html) {
4097         var doc = window.document;
4098         var container = doc.createElement("div");
4099         container.innerHTML = html;
4100         var frag = doc.createDocumentFragment(), n;
4101         while ((n = container.firstChild)) {
4102             frag.appendChild(n);
4103         }
4104         return frag;
4105     };
4106 }
4107
4108 /**
4109  * @class Roo.DomHelper
4110  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4111  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4112  * @singleton
4113  */
4114 Roo.DomHelper = function(){
4115     var tempTableEl = null;
4116     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4117     var tableRe = /^table|tbody|tr|td$/i;
4118     var xmlns = {};
4119     // build as innerHTML where available
4120     /** @ignore */
4121     var createHtml = function(o){
4122         if(typeof o == 'string'){
4123             return o;
4124         }
4125         var b = "";
4126         if(!o.tag){
4127             o.tag = "div";
4128         }
4129         b += "<" + o.tag;
4130         for(var attr in o){
4131             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4132             if(attr == "style"){
4133                 var s = o["style"];
4134                 if(typeof s == "function"){
4135                     s = s.call();
4136                 }
4137                 if(typeof s == "string"){
4138                     b += ' style="' + s + '"';
4139                 }else if(typeof s == "object"){
4140                     b += ' style="';
4141                     for(var key in s){
4142                         if(typeof s[key] != "function"){
4143                             b += key + ":" + s[key] + ";";
4144                         }
4145                     }
4146                     b += '"';
4147                 }
4148             }else{
4149                 if(attr == "cls"){
4150                     b += ' class="' + o["cls"] + '"';
4151                 }else if(attr == "htmlFor"){
4152                     b += ' for="' + o["htmlFor"] + '"';
4153                 }else{
4154                     b += " " + attr + '="' + o[attr] + '"';
4155                 }
4156             }
4157         }
4158         if(emptyTags.test(o.tag)){
4159             b += "/>";
4160         }else{
4161             b += ">";
4162             var cn = o.children || o.cn;
4163             if(cn){
4164                 //http://bugs.kde.org/show_bug.cgi?id=71506
4165                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4166                     for(var i = 0, len = cn.length; i < len; i++) {
4167                         b += createHtml(cn[i], b);
4168                     }
4169                 }else{
4170                     b += createHtml(cn, b);
4171                 }
4172             }
4173             if(o.html){
4174                 b += o.html;
4175             }
4176             b += "</" + o.tag + ">";
4177         }
4178         return b;
4179     };
4180
4181     // build as dom
4182     /** @ignore */
4183     var createDom = function(o, parentNode){
4184          
4185         // defininition craeted..
4186         var ns = false;
4187         if (o.ns && o.ns != 'html') {
4188                
4189             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4190                 xmlns[o.ns] = o.xmlns;
4191                 ns = o.xmlns;
4192             }
4193             if (typeof(xmlns[o.ns]) == 'undefined') {
4194                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4195             }
4196             ns = xmlns[o.ns];
4197         }
4198         
4199         
4200         if (typeof(o) == 'string') {
4201             return parentNode.appendChild(document.createTextNode(o));
4202         }
4203         o.tag = o.tag || div;
4204         if (o.ns && Roo.isIE) {
4205             ns = false;
4206             o.tag = o.ns + ':' + o.tag;
4207             
4208         }
4209         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4210         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4211         for(var attr in o){
4212             
4213             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4214                     attr == "style" || typeof o[attr] == "function") continue;
4215                     
4216             if(attr=="cls" && Roo.isIE){
4217                 el.className = o["cls"];
4218             }else{
4219                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4220                 else el[attr] = o[attr];
4221             }
4222         }
4223         Roo.DomHelper.applyStyles(el, o.style);
4224         var cn = o.children || o.cn;
4225         if(cn){
4226             //http://bugs.kde.org/show_bug.cgi?id=71506
4227              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4228                 for(var i = 0, len = cn.length; i < len; i++) {
4229                     createDom(cn[i], el);
4230                 }
4231             }else{
4232                 createDom(cn, el);
4233             }
4234         }
4235         if(o.html){
4236             el.innerHTML = o.html;
4237         }
4238         if(parentNode){
4239            parentNode.appendChild(el);
4240         }
4241         return el;
4242     };
4243
4244     var ieTable = function(depth, s, h, e){
4245         tempTableEl.innerHTML = [s, h, e].join('');
4246         var i = -1, el = tempTableEl;
4247         while(++i < depth){
4248             el = el.firstChild;
4249         }
4250         return el;
4251     };
4252
4253     // kill repeat to save bytes
4254     var ts = '<table>',
4255         te = '</table>',
4256         tbs = ts+'<tbody>',
4257         tbe = '</tbody>'+te,
4258         trs = tbs + '<tr>',
4259         tre = '</tr>'+tbe;
4260
4261     /**
4262      * @ignore
4263      * Nasty code for IE's broken table implementation
4264      */
4265     var insertIntoTable = function(tag, where, el, html){
4266         if(!tempTableEl){
4267             tempTableEl = document.createElement('div');
4268         }
4269         var node;
4270         var before = null;
4271         if(tag == 'td'){
4272             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4273                 return;
4274             }
4275             if(where == 'beforebegin'){
4276                 before = el;
4277                 el = el.parentNode;
4278             } else{
4279                 before = el.nextSibling;
4280                 el = el.parentNode;
4281             }
4282             node = ieTable(4, trs, html, tre);
4283         }
4284         else if(tag == 'tr'){
4285             if(where == 'beforebegin'){
4286                 before = el;
4287                 el = el.parentNode;
4288                 node = ieTable(3, tbs, html, tbe);
4289             } else if(where == 'afterend'){
4290                 before = el.nextSibling;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else{ // INTO a TR
4294                 if(where == 'afterbegin'){
4295                     before = el.firstChild;
4296                 }
4297                 node = ieTable(4, trs, html, tre);
4298             }
4299         } else if(tag == 'tbody'){
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303                 node = ieTable(2, ts, html, te);
4304             } else if(where == 'afterend'){
4305                 before = el.nextSibling;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else{
4309                 if(where == 'afterbegin'){
4310                     before = el.firstChild;
4311                 }
4312                 node = ieTable(3, tbs, html, tbe);
4313             }
4314         } else{ // TABLE
4315             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4316                 return;
4317             }
4318             if(where == 'afterbegin'){
4319                 before = el.firstChild;
4320             }
4321             node = ieTable(2, ts, html, te);
4322         }
4323         el.insertBefore(node, before);
4324         return node;
4325     };
4326
4327     return {
4328     /** True to force the use of DOM instead of html fragments @type Boolean */
4329     useDom : false,
4330
4331     /**
4332      * Returns the markup for the passed Element(s) config
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {String}
4335      */
4336     markup : function(o){
4337         return createHtml(o);
4338     },
4339
4340     /**
4341      * Applies a style specification to an element
4342      * @param {String/HTMLElement} el The element to apply styles to
4343      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4344      * a function which returns such a specification.
4345      */
4346     applyStyles : function(el, styles){
4347         if(styles){
4348            el = Roo.fly(el);
4349            if(typeof styles == "string"){
4350                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4351                var matches;
4352                while ((matches = re.exec(styles)) != null){
4353                    el.setStyle(matches[1], matches[2]);
4354                }
4355            }else if (typeof styles == "object"){
4356                for (var style in styles){
4357                   el.setStyle(style, styles[style]);
4358                }
4359            }else if (typeof styles == "function"){
4360                 Roo.DomHelper.applyStyles(el, styles.call());
4361            }
4362         }
4363     },
4364
4365     /**
4366      * Inserts an HTML fragment into the Dom
4367      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4368      * @param {HTMLElement} el The context element
4369      * @param {String} html The HTML fragmenet
4370      * @return {HTMLElement} The new node
4371      */
4372     insertHtml : function(where, el, html){
4373         where = where.toLowerCase();
4374         if(el.insertAdjacentHTML){
4375             if(tableRe.test(el.tagName)){
4376                 var rs;
4377                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4378                     return rs;
4379                 }
4380             }
4381             switch(where){
4382                 case "beforebegin":
4383                     el.insertAdjacentHTML('BeforeBegin', html);
4384                     return el.previousSibling;
4385                 case "afterbegin":
4386                     el.insertAdjacentHTML('AfterBegin', html);
4387                     return el.firstChild;
4388                 case "beforeend":
4389                     el.insertAdjacentHTML('BeforeEnd', html);
4390                     return el.lastChild;
4391                 case "afterend":
4392                     el.insertAdjacentHTML('AfterEnd', html);
4393                     return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396         }
4397         var range = el.ownerDocument.createRange();
4398         var frag;
4399         switch(where){
4400              case "beforebegin":
4401                 range.setStartBefore(el);
4402                 frag = range.createContextualFragment(html);
4403                 el.parentNode.insertBefore(frag, el);
4404                 return el.previousSibling;
4405              case "afterbegin":
4406                 if(el.firstChild){
4407                     range.setStartBefore(el.firstChild);
4408                     frag = range.createContextualFragment(html);
4409                     el.insertBefore(frag, el.firstChild);
4410                     return el.firstChild;
4411                 }else{
4412                     el.innerHTML = html;
4413                     return el.firstChild;
4414                 }
4415             case "beforeend":
4416                 if(el.lastChild){
4417                     range.setStartAfter(el.lastChild);
4418                     frag = range.createContextualFragment(html);
4419                     el.appendChild(frag);
4420                     return el.lastChild;
4421                 }else{
4422                     el.innerHTML = html;
4423                     return el.lastChild;
4424                 }
4425             case "afterend":
4426                 range.setStartAfter(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el.nextSibling);
4429                 return el.nextSibling;
4430             }
4431             throw 'Illegal insertion point -> "' + where + '"';
4432     },
4433
4434     /**
4435      * Creates new Dom element(s) and inserts them before el
4436      * @param {String/HTMLElement/Element} el The context element
4437      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4438      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4439      * @return {HTMLElement/Roo.Element} The new node
4440      */
4441     insertBefore : function(el, o, returnElement){
4442         return this.doInsert(el, o, returnElement, "beforeBegin");
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and inserts them after el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object} o The Dom object spec (and children)
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     insertAfter : function(el, o, returnElement){
4453         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4454     },
4455
4456     /**
4457      * Creates new Dom element(s) and inserts them as the first child of el
4458      * @param {String/HTMLElement/Element} el The context element
4459      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4460      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4461      * @return {HTMLElement/Roo.Element} The new node
4462      */
4463     insertFirst : function(el, o, returnElement){
4464         return this.doInsert(el, o, returnElement, "afterBegin");
4465     },
4466
4467     // private
4468     doInsert : function(el, o, returnElement, pos, sibling){
4469         el = Roo.getDom(el);
4470         var newNode;
4471         if(this.useDom || o.ns){
4472             newNode = createDom(o, null);
4473             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4474         }else{
4475             var html = createHtml(o);
4476             newNode = this.insertHtml(pos, el, html);
4477         }
4478         return returnElement ? Roo.get(newNode, true) : newNode;
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and appends them to el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     append : function(el, o, returnElement){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.appendChild(newNode);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml("beforeEnd", el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and overwrites the contents of el with them
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     overwrite : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         if (o.ns) {
4511           
4512             while (el.childNodes.length) {
4513                 el.removeChild(el.firstChild);
4514             }
4515             createDom(o, el);
4516         } else {
4517             el.innerHTML = createHtml(o);   
4518         }
4519         
4520         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4521     },
4522
4523     /**
4524      * Creates a new Roo.DomHelper.Template from the Dom object spec
4525      * @param {Object} o The Dom object spec (and children)
4526      * @return {Roo.DomHelper.Template} The new template
4527      */
4528     createTemplate : function(o){
4529         var html = createHtml(o);
4530         return new Roo.Template(html);
4531     }
4532     };
4533 }();
4534 /*
4535  * Based on:
4536  * Ext JS Library 1.1.1
4537  * Copyright(c) 2006-2007, Ext JS, LLC.
4538  *
4539  * Originally Released Under LGPL - original licence link has changed is not relivant.
4540  *
4541  * Fork - LGPL
4542  * <script type="text/javascript">
4543  */
4544  
4545 /**
4546 * @class Roo.Template
4547 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4548 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4549 * Usage:
4550 <pre><code>
4551 var t = new Roo.Template({
4552     html :  '&lt;div name="{id}"&gt;' + 
4553         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4554         '&lt;/div&gt;',
4555     myformat: function (value, allValues) {
4556         return 'XX' + value;
4557     }
4558 });
4559 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4560 </code></pre>
4561 * For more information see this blog post with examples:
4562 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4563      - Create Elements using DOM, HTML fragments and Templates</a>. 
4564 * @constructor
4565 * @param {Object} cfg - Configuration object.
4566 */
4567 Roo.Template = function(cfg){
4568     // BC!
4569     if(cfg instanceof Array){
4570         cfg = cfg.join("");
4571     }else if(arguments.length > 1){
4572         cfg = Array.prototype.join.call(arguments, "");
4573     }
4574     
4575     
4576     if (typeof(cfg) == 'object') {
4577         Roo.apply(this,cfg)
4578     } else {
4579         // bc
4580         this.html = cfg;
4581     }
4582     if (this.url) {
4583         this.load();
4584     }
4585     
4586 };
4587 Roo.Template.prototype = {
4588     
4589     /**
4590      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4591      *                    it should be fixed so that template is observable...
4592      */
4593     url : false,
4594     /**
4595      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4596      */
4597     html : '',
4598     /**
4599      * Returns an HTML fragment of this template with the specified values applied.
4600      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4601      * @return {String} The HTML fragment
4602      */
4603     applyTemplate : function(values){
4604         try {
4605            
4606             if(this.compiled){
4607                 return this.compiled(values);
4608             }
4609             var useF = this.disableFormats !== true;
4610             var fm = Roo.util.Format, tpl = this;
4611             var fn = function(m, name, format, args){
4612                 if(format && useF){
4613                     if(format.substr(0, 5) == "this."){
4614                         return tpl.call(format.substr(5), values[name], values);
4615                     }else{
4616                         if(args){
4617                             // quoted values are required for strings in compiled templates, 
4618                             // but for non compiled we need to strip them
4619                             // quoted reversed for jsmin
4620                             var re = /^\s*['"](.*)["']\s*$/;
4621                             args = args.split(',');
4622                             for(var i = 0, len = args.length; i < len; i++){
4623                                 args[i] = args[i].replace(re, "$1");
4624                             }
4625                             args = [values[name]].concat(args);
4626                         }else{
4627                             args = [values[name]];
4628                         }
4629                         return fm[format].apply(fm, args);
4630                     }
4631                 }else{
4632                     return values[name] !== undefined ? values[name] : "";
4633                 }
4634             };
4635             return this.html.replace(this.re, fn);
4636         } catch (e) {
4637             Roo.log(e);
4638             throw e;
4639         }
4640          
4641     },
4642     
4643     loading : false,
4644       
4645     load : function ()
4646     {
4647          
4648         if (this.loading) {
4649             return;
4650         }
4651         var _t = this;
4652         
4653         this.loading = true;
4654         this.compiled = false;
4655         
4656         var cx = new Roo.data.Connection();
4657         cx.request({
4658             url : this.url,
4659             method : 'GET',
4660             success : function (response) {
4661                 _t.loading = false;
4662                 _t.html = response.responseText;
4663                 _t.url = false;
4664                 _t.compile();
4665              },
4666             failure : function(response) {
4667                 Roo.log("Template failed to load from " + _t.url);
4668                 _t.loading = false;
4669             }
4670         });
4671     },
4672
4673     /**
4674      * Sets the HTML used as the template and optionally compiles it.
4675      * @param {String} html
4676      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4677      * @return {Roo.Template} this
4678      */
4679     set : function(html, compile){
4680         this.html = html;
4681         this.compiled = null;
4682         if(compile){
4683             this.compile();
4684         }
4685         return this;
4686     },
4687     
4688     /**
4689      * True to disable format functions (defaults to false)
4690      * @type Boolean
4691      */
4692     disableFormats : false,
4693     
4694     /**
4695     * The regular expression used to match template variables 
4696     * @type RegExp
4697     * @property 
4698     */
4699     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4700     
4701     /**
4702      * Compiles the template into an internal function, eliminating the RegEx overhead.
4703      * @return {Roo.Template} this
4704      */
4705     compile : function(){
4706         var fm = Roo.util.Format;
4707         var useF = this.disableFormats !== true;
4708         var sep = Roo.isGecko ? "+" : ",";
4709         var fn = function(m, name, format, args){
4710             if(format && useF){
4711                 args = args ? ',' + args : "";
4712                 if(format.substr(0, 5) != "this."){
4713                     format = "fm." + format + '(';
4714                 }else{
4715                     format = 'this.call("'+ format.substr(5) + '", ';
4716                     args = ", values";
4717                 }
4718             }else{
4719                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4720             }
4721             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4722         };
4723         var body;
4724         // branched to use + in gecko and [].join() in others
4725         if(Roo.isGecko){
4726             body = "this.compiled = function(values){ return '" +
4727                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4728                     "';};";
4729         }else{
4730             body = ["this.compiled = function(values){ return ['"];
4731             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4732             body.push("'].join('');};");
4733             body = body.join('');
4734         }
4735         /**
4736          * eval:var:values
4737          * eval:var:fm
4738          */
4739         eval(body);
4740         return this;
4741     },
4742     
4743     // private function used to call members
4744     call : function(fnName, value, allValues){
4745         return this[fnName](value, allValues);
4746     },
4747     
4748     /**
4749      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4750      * @param {String/HTMLElement/Roo.Element} el The context element
4751      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4752      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4753      * @return {HTMLElement/Roo.Element} The new node or Element
4754      */
4755     insertFirst: function(el, values, returnElement){
4756         return this.doInsert('afterBegin', el, values, returnElement);
4757     },
4758
4759     /**
4760      * Applies the supplied values to the template and inserts the new node(s) before el.
4761      * @param {String/HTMLElement/Roo.Element} el The context element
4762      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4763      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4764      * @return {HTMLElement/Roo.Element} The new node or Element
4765      */
4766     insertBefore: function(el, values, returnElement){
4767         return this.doInsert('beforeBegin', el, values, returnElement);
4768     },
4769
4770     /**
4771      * Applies the supplied values to the template and inserts the new node(s) after el.
4772      * @param {String/HTMLElement/Roo.Element} el The context element
4773      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4774      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4775      * @return {HTMLElement/Roo.Element} The new node or Element
4776      */
4777     insertAfter : function(el, values, returnElement){
4778         return this.doInsert('afterEnd', el, values, returnElement);
4779     },
4780     
4781     /**
4782      * Applies the supplied values to the template and appends the new node(s) to el.
4783      * @param {String/HTMLElement/Roo.Element} el The context element
4784      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4785      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4786      * @return {HTMLElement/Roo.Element} The new node or Element
4787      */
4788     append : function(el, values, returnElement){
4789         return this.doInsert('beforeEnd', el, values, returnElement);
4790     },
4791
4792     doInsert : function(where, el, values, returnEl){
4793         el = Roo.getDom(el);
4794         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4795         return returnEl ? Roo.get(newNode, true) : newNode;
4796     },
4797
4798     /**
4799      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4800      * @param {String/HTMLElement/Roo.Element} el The context element
4801      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4802      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4803      * @return {HTMLElement/Roo.Element} The new node or Element
4804      */
4805     overwrite : function(el, values, returnElement){
4806         el = Roo.getDom(el);
4807         el.innerHTML = this.applyTemplate(values);
4808         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4809     }
4810 };
4811 /**
4812  * Alias for {@link #applyTemplate}
4813  * @method
4814  */
4815 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4816
4817 // backwards compat
4818 Roo.DomHelper.Template = Roo.Template;
4819
4820 /**
4821  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4822  * @param {String/HTMLElement} el A DOM element or its id
4823  * @returns {Roo.Template} The created template
4824  * @static
4825  */
4826 Roo.Template.from = function(el){
4827     el = Roo.getDom(el);
4828     return new Roo.Template(el.value || el.innerHTML);
4829 };/*
4830  * Based on:
4831  * Ext JS Library 1.1.1
4832  * Copyright(c) 2006-2007, Ext JS, LLC.
4833  *
4834  * Originally Released Under LGPL - original licence link has changed is not relivant.
4835  *
4836  * Fork - LGPL
4837  * <script type="text/javascript">
4838  */
4839  
4840
4841 /*
4842  * This is code is also distributed under MIT license for use
4843  * with jQuery and prototype JavaScript libraries.
4844  */
4845 /**
4846  * @class Roo.DomQuery
4847 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4848 <p>
4849 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4850
4851 <p>
4852 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4853 </p>
4854 <h4>Element Selectors:</h4>
4855 <ul class="list">
4856     <li> <b>*</b> any element</li>
4857     <li> <b>E</b> an element with the tag E</li>
4858     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4859     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4860     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4861     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4862 </ul>
4863 <h4>Attribute Selectors:</h4>
4864 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4865 <ul class="list">
4866     <li> <b>E[foo]</b> has an attribute "foo"</li>
4867     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4868     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4869     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4870     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4871     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4872     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4873 </ul>
4874 <h4>Pseudo Classes:</h4>
4875 <ul class="list">
4876     <li> <b>E:first-child</b> E is the first child of its parent</li>
4877     <li> <b>E:last-child</b> E is the last child of its parent</li>
4878     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4879     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4880     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4881     <li> <b>E:only-child</b> E is the only child of its parent</li>
4882     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4883     <li> <b>E:first</b> the first E in the resultset</li>
4884     <li> <b>E:last</b> the last E in the resultset</li>
4885     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4886     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4887     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4888     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4889     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4890     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4891     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4892     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4893     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4894 </ul>
4895 <h4>CSS Value Selectors:</h4>
4896 <ul class="list">
4897     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4898     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4899     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4900     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4901     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4902     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4903 </ul>
4904  * @singleton
4905  */
4906 Roo.DomQuery = function(){
4907     var cache = {}, simpleCache = {}, valueCache = {};
4908     var nonSpace = /\S/;
4909     var trimRe = /^\s+|\s+$/g;
4910     var tplRe = /\{(\d+)\}/g;
4911     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4912     var tagTokenRe = /^(#)?([\w-\*]+)/;
4913     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4914
4915     function child(p, index){
4916         var i = 0;
4917         var n = p.firstChild;
4918         while(n){
4919             if(n.nodeType == 1){
4920                if(++i == index){
4921                    return n;
4922                }
4923             }
4924             n = n.nextSibling;
4925         }
4926         return null;
4927     };
4928
4929     function next(n){
4930         while((n = n.nextSibling) && n.nodeType != 1);
4931         return n;
4932     };
4933
4934     function prev(n){
4935         while((n = n.previousSibling) && n.nodeType != 1);
4936         return n;
4937     };
4938
4939     function children(d){
4940         var n = d.firstChild, ni = -1;
4941             while(n){
4942                 var nx = n.nextSibling;
4943                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4944                     d.removeChild(n);
4945                 }else{
4946                     n.nodeIndex = ++ni;
4947                 }
4948                 n = nx;
4949             }
4950             return this;
4951         };
4952
4953     function byClassName(c, a, v){
4954         if(!v){
4955             return c;
4956         }
4957         var r = [], ri = -1, cn;
4958         for(var i = 0, ci; ci = c[i]; i++){
4959             if((' '+ci.className+' ').indexOf(v) != -1){
4960                 r[++ri] = ci;
4961             }
4962         }
4963         return r;
4964     };
4965
4966     function attrValue(n, attr){
4967         if(!n.tagName && typeof n.length != "undefined"){
4968             n = n[0];
4969         }
4970         if(!n){
4971             return null;
4972         }
4973         if(attr == "for"){
4974             return n.htmlFor;
4975         }
4976         if(attr == "class" || attr == "className"){
4977             return n.className;
4978         }
4979         return n.getAttribute(attr) || n[attr];
4980
4981     };
4982
4983     function getNodes(ns, mode, tagName){
4984         var result = [], ri = -1, cs;
4985         if(!ns){
4986             return result;
4987         }
4988         tagName = tagName || "*";
4989         if(typeof ns.getElementsByTagName != "undefined"){
4990             ns = [ns];
4991         }
4992         if(!mode){
4993             for(var i = 0, ni; ni = ns[i]; i++){
4994                 cs = ni.getElementsByTagName(tagName);
4995                 for(var j = 0, ci; ci = cs[j]; j++){
4996                     result[++ri] = ci;
4997                 }
4998             }
4999         }else if(mode == "/" || mode == ">"){
5000             var utag = tagName.toUpperCase();
5001             for(var i = 0, ni, cn; ni = ns[i]; i++){
5002                 cn = ni.children || ni.childNodes;
5003                 for(var j = 0, cj; cj = cn[j]; j++){
5004                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5005                         result[++ri] = cj;
5006                     }
5007                 }
5008             }
5009         }else if(mode == "+"){
5010             var utag = tagName.toUpperCase();
5011             for(var i = 0, n; n = ns[i]; i++){
5012                 while((n = n.nextSibling) && n.nodeType != 1);
5013                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5014                     result[++ri] = n;
5015                 }
5016             }
5017         }else if(mode == "~"){
5018             for(var i = 0, n; n = ns[i]; i++){
5019                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5020                 if(n){
5021                     result[++ri] = n;
5022                 }
5023             }
5024         }
5025         return result;
5026     };
5027
5028     function concat(a, b){
5029         if(b.slice){
5030             return a.concat(b);
5031         }
5032         for(var i = 0, l = b.length; i < l; i++){
5033             a[a.length] = b[i];
5034         }
5035         return a;
5036     }
5037
5038     function byTag(cs, tagName){
5039         if(cs.tagName || cs == document){
5040             cs = [cs];
5041         }
5042         if(!tagName){
5043             return cs;
5044         }
5045         var r = [], ri = -1;
5046         tagName = tagName.toLowerCase();
5047         for(var i = 0, ci; ci = cs[i]; i++){
5048             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5049                 r[++ri] = ci;
5050             }
5051         }
5052         return r;
5053     };
5054
5055     function byId(cs, attr, id){
5056         if(cs.tagName || cs == document){
5057             cs = [cs];
5058         }
5059         if(!id){
5060             return cs;
5061         }
5062         var r = [], ri = -1;
5063         for(var i = 0,ci; ci = cs[i]; i++){
5064             if(ci && ci.id == id){
5065                 r[++ri] = ci;
5066                 return r;
5067             }
5068         }
5069         return r;
5070     };
5071
5072     function byAttribute(cs, attr, value, op, custom){
5073         var r = [], ri = -1, st = custom=="{";
5074         var f = Roo.DomQuery.operators[op];
5075         for(var i = 0, ci; ci = cs[i]; i++){
5076             var a;
5077             if(st){
5078                 a = Roo.DomQuery.getStyle(ci, attr);
5079             }
5080             else if(attr == "class" || attr == "className"){
5081                 a = ci.className;
5082             }else if(attr == "for"){
5083                 a = ci.htmlFor;
5084             }else if(attr == "href"){
5085                 a = ci.getAttribute("href", 2);
5086             }else{
5087                 a = ci.getAttribute(attr);
5088             }
5089             if((f && f(a, value)) || (!f && a)){
5090                 r[++ri] = ci;
5091             }
5092         }
5093         return r;
5094     };
5095
5096     function byPseudo(cs, name, value){
5097         return Roo.DomQuery.pseudos[name](cs, value);
5098     };
5099
5100     // This is for IE MSXML which does not support expandos.
5101     // IE runs the same speed using setAttribute, however FF slows way down
5102     // and Safari completely fails so they need to continue to use expandos.
5103     var isIE = window.ActiveXObject ? true : false;
5104
5105     // this eval is stop the compressor from
5106     // renaming the variable to something shorter
5107     
5108     /** eval:var:batch */
5109     var batch = 30803; 
5110
5111     var key = 30803;
5112
5113     function nodupIEXml(cs){
5114         var d = ++key;
5115         cs[0].setAttribute("_nodup", d);
5116         var r = [cs[0]];
5117         for(var i = 1, len = cs.length; i < len; i++){
5118             var c = cs[i];
5119             if(!c.getAttribute("_nodup") != d){
5120                 c.setAttribute("_nodup", d);
5121                 r[r.length] = c;
5122             }
5123         }
5124         for(var i = 0, len = cs.length; i < len; i++){
5125             cs[i].removeAttribute("_nodup");
5126         }
5127         return r;
5128     }
5129
5130     function nodup(cs){
5131         if(!cs){
5132             return [];
5133         }
5134         var len = cs.length, c, i, r = cs, cj, ri = -1;
5135         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5136             return cs;
5137         }
5138         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5139             return nodupIEXml(cs);
5140         }
5141         var d = ++key;
5142         cs[0]._nodup = d;
5143         for(i = 1; c = cs[i]; i++){
5144             if(c._nodup != d){
5145                 c._nodup = d;
5146             }else{
5147                 r = [];
5148                 for(var j = 0; j < i; j++){
5149                     r[++ri] = cs[j];
5150                 }
5151                 for(j = i+1; cj = cs[j]; j++){
5152                     if(cj._nodup != d){
5153                         cj._nodup = d;
5154                         r[++ri] = cj;
5155                     }
5156                 }
5157                 return r;
5158             }
5159         }
5160         return r;
5161     }
5162
5163     function quickDiffIEXml(c1, c2){
5164         var d = ++key;
5165         for(var i = 0, len = c1.length; i < len; i++){
5166             c1[i].setAttribute("_qdiff", d);
5167         }
5168         var r = [];
5169         for(var i = 0, len = c2.length; i < len; i++){
5170             if(c2[i].getAttribute("_qdiff") != d){
5171                 r[r.length] = c2[i];
5172             }
5173         }
5174         for(var i = 0, len = c1.length; i < len; i++){
5175            c1[i].removeAttribute("_qdiff");
5176         }
5177         return r;
5178     }
5179
5180     function quickDiff(c1, c2){
5181         var len1 = c1.length;
5182         if(!len1){
5183             return c2;
5184         }
5185         if(isIE && c1[0].selectSingleNode){
5186             return quickDiffIEXml(c1, c2);
5187         }
5188         var d = ++key;
5189         for(var i = 0; i < len1; i++){
5190             c1[i]._qdiff = d;
5191         }
5192         var r = [];
5193         for(var i = 0, len = c2.length; i < len; i++){
5194             if(c2[i]._qdiff != d){
5195                 r[r.length] = c2[i];
5196             }
5197         }
5198         return r;
5199     }
5200
5201     function quickId(ns, mode, root, id){
5202         if(ns == root){
5203            var d = root.ownerDocument || root;
5204            return d.getElementById(id);
5205         }
5206         ns = getNodes(ns, mode, "*");
5207         return byId(ns, null, id);
5208     }
5209
5210     return {
5211         getStyle : function(el, name){
5212             return Roo.fly(el).getStyle(name);
5213         },
5214         /**
5215          * Compiles a selector/xpath query into a reusable function. The returned function
5216          * takes one parameter "root" (optional), which is the context node from where the query should start.
5217          * @param {String} selector The selector/xpath query
5218          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5219          * @return {Function}
5220          */
5221         compile : function(path, type){
5222             type = type || "select";
5223             
5224             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5225             var q = path, mode, lq;
5226             var tk = Roo.DomQuery.matchers;
5227             var tklen = tk.length;
5228             var mm;
5229
5230             // accept leading mode switch
5231             var lmode = q.match(modeRe);
5232             if(lmode && lmode[1]){
5233                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5234                 q = q.replace(lmode[1], "");
5235             }
5236             // strip leading slashes
5237             while(path.substr(0, 1)=="/"){
5238                 path = path.substr(1);
5239             }
5240
5241             while(q && lq != q){
5242                 lq = q;
5243                 var tm = q.match(tagTokenRe);
5244                 if(type == "select"){
5245                     if(tm){
5246                         if(tm[1] == "#"){
5247                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5248                         }else{
5249                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5250                         }
5251                         q = q.replace(tm[0], "");
5252                     }else if(q.substr(0, 1) != '@'){
5253                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5254                     }
5255                 }else{
5256                     if(tm){
5257                         if(tm[1] == "#"){
5258                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5259                         }else{
5260                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5261                         }
5262                         q = q.replace(tm[0], "");
5263                     }
5264                 }
5265                 while(!(mm = q.match(modeRe))){
5266                     var matched = false;
5267                     for(var j = 0; j < tklen; j++){
5268                         var t = tk[j];
5269                         var m = q.match(t.re);
5270                         if(m){
5271                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5272                                                     return m[i];
5273                                                 });
5274                             q = q.replace(m[0], "");
5275                             matched = true;
5276                             break;
5277                         }
5278                     }
5279                     // prevent infinite loop on bad selector
5280                     if(!matched){
5281                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5282                     }
5283                 }
5284                 if(mm[1]){
5285                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5286                     q = q.replace(mm[1], "");
5287                 }
5288             }
5289             fn[fn.length] = "return nodup(n);\n}";
5290             
5291              /** 
5292               * list of variables that need from compression as they are used by eval.
5293              *  eval:var:batch 
5294              *  eval:var:nodup
5295              *  eval:var:byTag
5296              *  eval:var:ById
5297              *  eval:var:getNodes
5298              *  eval:var:quickId
5299              *  eval:var:mode
5300              *  eval:var:root
5301              *  eval:var:n
5302              *  eval:var:byClassName
5303              *  eval:var:byPseudo
5304              *  eval:var:byAttribute
5305              *  eval:var:attrValue
5306              * 
5307              **/ 
5308             eval(fn.join(""));
5309             return f;
5310         },
5311
5312         /**
5313          * Selects a group of elements.
5314          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5315          * @param {Node} root (optional) The start of the query (defaults to document).
5316          * @return {Array}
5317          */
5318         select : function(path, root, type){
5319             if(!root || root == document){
5320                 root = document;
5321             }
5322             if(typeof root == "string"){
5323                 root = document.getElementById(root);
5324             }
5325             var paths = path.split(",");
5326             var results = [];
5327             for(var i = 0, len = paths.length; i < len; i++){
5328                 var p = paths[i].replace(trimRe, "");
5329                 if(!cache[p]){
5330                     cache[p] = Roo.DomQuery.compile(p);
5331                     if(!cache[p]){
5332                         throw p + " is not a valid selector";
5333                     }
5334                 }
5335                 var result = cache[p](root);
5336                 if(result && result != document){
5337                     results = results.concat(result);
5338                 }
5339             }
5340             if(paths.length > 1){
5341                 return nodup(results);
5342             }
5343             return results;
5344         },
5345
5346         /**
5347          * Selects a single element.
5348          * @param {String} selector The selector/xpath query
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Element}
5351          */
5352         selectNode : function(path, root){
5353             return Roo.DomQuery.select(path, root)[0];
5354         },
5355
5356         /**
5357          * Selects the value of a node, optionally replacing null with the defaultValue.
5358          * @param {String} selector The selector/xpath query
5359          * @param {Node} root (optional) The start of the query (defaults to document).
5360          * @param {String} defaultValue
5361          */
5362         selectValue : function(path, root, defaultValue){
5363             path = path.replace(trimRe, "");
5364             if(!valueCache[path]){
5365                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5366             }
5367             var n = valueCache[path](root);
5368             n = n[0] ? n[0] : n;
5369             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5370             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5371         },
5372
5373         /**
5374          * Selects the value of a node, parsing integers and floats.
5375          * @param {String} selector The selector/xpath query
5376          * @param {Node} root (optional) The start of the query (defaults to document).
5377          * @param {Number} defaultValue
5378          * @return {Number}
5379          */
5380         selectNumber : function(path, root, defaultValue){
5381             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5382             return parseFloat(v);
5383         },
5384
5385         /**
5386          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5387          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5388          * @param {String} selector The simple selector to test
5389          * @return {Boolean}
5390          */
5391         is : function(el, ss){
5392             if(typeof el == "string"){
5393                 el = document.getElementById(el);
5394             }
5395             var isArray = (el instanceof Array);
5396             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5397             return isArray ? (result.length == el.length) : (result.length > 0);
5398         },
5399
5400         /**
5401          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5402          * @param {Array} el An array of elements to filter
5403          * @param {String} selector The simple selector to test
5404          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5405          * the selector instead of the ones that match
5406          * @return {Array}
5407          */
5408         filter : function(els, ss, nonMatches){
5409             ss = ss.replace(trimRe, "");
5410             if(!simpleCache[ss]){
5411                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5412             }
5413             var result = simpleCache[ss](els);
5414             return nonMatches ? quickDiff(result, els) : result;
5415         },
5416
5417         /**
5418          * Collection of matching regular expressions and code snippets.
5419          */
5420         matchers : [{
5421                 re: /^\.([\w-]+)/,
5422                 select: 'n = byClassName(n, null, " {1} ");'
5423             }, {
5424                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5425                 select: 'n = byPseudo(n, "{1}", "{2}");'
5426             },{
5427                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5428                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5429             }, {
5430                 re: /^#([\w-]+)/,
5431                 select: 'n = byId(n, null, "{1}");'
5432             },{
5433                 re: /^@([\w-]+)/,
5434                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5435             }
5436         ],
5437
5438         /**
5439          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5440          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5441          */
5442         operators : {
5443             "=" : function(a, v){
5444                 return a == v;
5445             },
5446             "!=" : function(a, v){
5447                 return a != v;
5448             },
5449             "^=" : function(a, v){
5450                 return a && a.substr(0, v.length) == v;
5451             },
5452             "$=" : function(a, v){
5453                 return a && a.substr(a.length-v.length) == v;
5454             },
5455             "*=" : function(a, v){
5456                 return a && a.indexOf(v) !== -1;
5457             },
5458             "%=" : function(a, v){
5459                 return (a % v) == 0;
5460             },
5461             "|=" : function(a, v){
5462                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5463             },
5464             "~=" : function(a, v){
5465                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5466             }
5467         },
5468
5469         /**
5470          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5471          * and the argument (if any) supplied in the selector.
5472          */
5473         pseudos : {
5474             "first-child" : function(c){
5475                 var r = [], ri = -1, n;
5476                 for(var i = 0, ci; ci = n = c[i]; i++){
5477                     while((n = n.previousSibling) && n.nodeType != 1);
5478                     if(!n){
5479                         r[++ri] = ci;
5480                     }
5481                 }
5482                 return r;
5483             },
5484
5485             "last-child" : function(c){
5486                 var r = [], ri = -1, n;
5487                 for(var i = 0, ci; ci = n = c[i]; i++){
5488                     while((n = n.nextSibling) && n.nodeType != 1);
5489                     if(!n){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "nth-child" : function(c, a) {
5497                 var r = [], ri = -1;
5498                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5499                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5500                 for(var i = 0, n; n = c[i]; i++){
5501                     var pn = n.parentNode;
5502                     if (batch != pn._batch) {
5503                         var j = 0;
5504                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5505                             if(cn.nodeType == 1){
5506                                cn.nodeIndex = ++j;
5507                             }
5508                         }
5509                         pn._batch = batch;
5510                     }
5511                     if (f == 1) {
5512                         if (l == 0 || n.nodeIndex == l){
5513                             r[++ri] = n;
5514                         }
5515                     } else if ((n.nodeIndex + l) % f == 0){
5516                         r[++ri] = n;
5517                     }
5518                 }
5519
5520                 return r;
5521             },
5522
5523             "only-child" : function(c){
5524                 var r = [], ri = -1;;
5525                 for(var i = 0, ci; ci = c[i]; i++){
5526                     if(!prev(ci) && !next(ci)){
5527                         r[++ri] = ci;
5528                     }
5529                 }
5530                 return r;
5531             },
5532
5533             "empty" : function(c){
5534                 var r = [], ri = -1;
5535                 for(var i = 0, ci; ci = c[i]; i++){
5536                     var cns = ci.childNodes, j = 0, cn, empty = true;
5537                     while(cn = cns[j]){
5538                         ++j;
5539                         if(cn.nodeType == 1 || cn.nodeType == 3){
5540                             empty = false;
5541                             break;
5542                         }
5543                     }
5544                     if(empty){
5545                         r[++ri] = ci;
5546                     }
5547                 }
5548                 return r;
5549             },
5550
5551             "contains" : function(c, v){
5552                 var r = [], ri = -1;
5553                 for(var i = 0, ci; ci = c[i]; i++){
5554                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5555                         r[++ri] = ci;
5556                     }
5557                 }
5558                 return r;
5559             },
5560
5561             "nodeValue" : function(c, v){
5562                 var r = [], ri = -1;
5563                 for(var i = 0, ci; ci = c[i]; i++){
5564                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "checked" : function(c){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if(ci.checked == true){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "not" : function(c, ss){
5582                 return Roo.DomQuery.filter(c, ss, true);
5583             },
5584
5585             "odd" : function(c){
5586                 return this["nth-child"](c, "odd");
5587             },
5588
5589             "even" : function(c){
5590                 return this["nth-child"](c, "even");
5591             },
5592
5593             "nth" : function(c, a){
5594                 return c[a-1] || [];
5595             },
5596
5597             "first" : function(c){
5598                 return c[0] || [];
5599             },
5600
5601             "last" : function(c){
5602                 return c[c.length-1] || [];
5603             },
5604
5605             "has" : function(c, ss){
5606                 var s = Roo.DomQuery.select;
5607                 var r = [], ri = -1;
5608                 for(var i = 0, ci; ci = c[i]; i++){
5609                     if(s(ss, ci).length > 0){
5610                         r[++ri] = ci;
5611                     }
5612                 }
5613                 return r;
5614             },
5615
5616             "next" : function(c, ss){
5617                 var is = Roo.DomQuery.is;
5618                 var r = [], ri = -1;
5619                 for(var i = 0, ci; ci = c[i]; i++){
5620                     var n = next(ci);
5621                     if(n && is(n, ss)){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "prev" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = prev(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             }
5639         }
5640     };
5641 }();
5642
5643 /**
5644  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5645  * @param {String} path The selector/xpath query
5646  * @param {Node} root (optional) The start of the query (defaults to document).
5647  * @return {Array}
5648  * @member Roo
5649  * @method query
5650  */
5651 Roo.query = Roo.DomQuery.select;
5652 /*
5653  * Based on:
5654  * Ext JS Library 1.1.1
5655  * Copyright(c) 2006-2007, Ext JS, LLC.
5656  *
5657  * Originally Released Under LGPL - original licence link has changed is not relivant.
5658  *
5659  * Fork - LGPL
5660  * <script type="text/javascript">
5661  */
5662
5663 /**
5664  * @class Roo.util.Observable
5665  * Base class that provides a common interface for publishing events. Subclasses are expected to
5666  * to have a property "events" with all the events defined.<br>
5667  * For example:
5668  * <pre><code>
5669  Employee = function(name){
5670     this.name = name;
5671     this.addEvents({
5672         "fired" : true,
5673         "quit" : true
5674     });
5675  }
5676  Roo.extend(Employee, Roo.util.Observable);
5677 </code></pre>
5678  * @param {Object} config properties to use (incuding events / listeners)
5679  */
5680
5681 Roo.util.Observable = function(cfg){
5682     
5683     cfg = cfg|| {};
5684     this.addEvents(cfg.events || {});
5685     if (cfg.events) {
5686         delete cfg.events; // make sure
5687     }
5688      
5689     Roo.apply(this, cfg);
5690     
5691     if(this.listeners){
5692         this.on(this.listeners);
5693         delete this.listeners;
5694     }
5695 };
5696 Roo.util.Observable.prototype = {
5697     /** 
5698  * @cfg {Object} listeners  list of events and functions to call for this object, 
5699  * For example :
5700  * <pre><code>
5701     listeners :  { 
5702        'click' : function(e) {
5703            ..... 
5704         } ,
5705         .... 
5706     } 
5707   </code></pre>
5708  */
5709     
5710     
5711     /**
5712      * Fires the specified event with the passed parameters (minus the event name).
5713      * @param {String} eventName
5714      * @param {Object...} args Variable number of parameters are passed to handlers
5715      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5716      */
5717     fireEvent : function(){
5718         var ce = this.events[arguments[0].toLowerCase()];
5719         if(typeof ce == "object"){
5720             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5721         }else{
5722             return true;
5723         }
5724     },
5725
5726     // private
5727     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5728
5729     /**
5730      * Appends an event handler to this component
5731      * @param {String}   eventName The type of event to listen for
5732      * @param {Function} handler The method the event invokes
5733      * @param {Object}   scope (optional) The scope in which to execute the handler
5734      * function. The handler function's "this" context.
5735      * @param {Object}   options (optional) An object containing handler configuration
5736      * properties. This may contain any of the following properties:<ul>
5737      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5738      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5739      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5740      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5741      * by the specified number of milliseconds. If the event fires again within that time, the original
5742      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5743      * </ul><br>
5744      * <p>
5745      * <b>Combining Options</b><br>
5746      * Using the options argument, it is possible to combine different types of listeners:<br>
5747      * <br>
5748      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5749                 <pre><code>
5750                 el.on('click', this.onClick, this, {
5751                         single: true,
5752                 delay: 100,
5753                 forumId: 4
5754                 });
5755                 </code></pre>
5756      * <p>
5757      * <b>Attaching multiple handlers in 1 call</b><br>
5758      * The method also allows for a single argument to be passed which is a config object containing properties
5759      * which specify multiple handlers.
5760      * <pre><code>
5761                 el.on({
5762                         'click': {
5763                         fn: this.onClick,
5764                         scope: this,
5765                         delay: 100
5766                 }, 
5767                 'mouseover': {
5768                         fn: this.onMouseOver,
5769                         scope: this
5770                 },
5771                 'mouseout': {
5772                         fn: this.onMouseOut,
5773                         scope: this
5774                 }
5775                 });
5776                 </code></pre>
5777      * <p>
5778      * Or a shorthand syntax which passes the same scope object to all handlers:
5779         <pre><code>
5780                 el.on({
5781                         'click': this.onClick,
5782                 'mouseover': this.onMouseOver,
5783                 'mouseout': this.onMouseOut,
5784                 scope: this
5785                 });
5786                 </code></pre>
5787      */
5788     addListener : function(eventName, fn, scope, o){
5789         if(typeof eventName == "object"){
5790             o = eventName;
5791             for(var e in o){
5792                 if(this.filterOptRe.test(e)){
5793                     continue;
5794                 }
5795                 if(typeof o[e] == "function"){
5796                     // shared options
5797                     this.addListener(e, o[e], o.scope,  o);
5798                 }else{
5799                     // individual options
5800                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5801                 }
5802             }
5803             return;
5804         }
5805         o = (!o || typeof o == "boolean") ? {} : o;
5806         eventName = eventName.toLowerCase();
5807         var ce = this.events[eventName] || true;
5808         if(typeof ce == "boolean"){
5809             ce = new Roo.util.Event(this, eventName);
5810             this.events[eventName] = ce;
5811         }
5812         ce.addListener(fn, scope, o);
5813     },
5814
5815     /**
5816      * Removes a listener
5817      * @param {String}   eventName     The type of event to listen for
5818      * @param {Function} handler        The handler to remove
5819      * @param {Object}   scope  (optional) The scope (this object) for the handler
5820      */
5821     removeListener : function(eventName, fn, scope){
5822         var ce = this.events[eventName.toLowerCase()];
5823         if(typeof ce == "object"){
5824             ce.removeListener(fn, scope);
5825         }
5826     },
5827
5828     /**
5829      * Removes all listeners for this object
5830      */
5831     purgeListeners : function(){
5832         for(var evt in this.events){
5833             if(typeof this.events[evt] == "object"){
5834                  this.events[evt].clearListeners();
5835             }
5836         }
5837     },
5838
5839     relayEvents : function(o, events){
5840         var createHandler = function(ename){
5841             return function(){
5842                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5843             };
5844         };
5845         for(var i = 0, len = events.length; i < len; i++){
5846             var ename = events[i];
5847             if(!this.events[ename]){ this.events[ename] = true; };
5848             o.on(ename, createHandler(ename), this);
5849         }
5850     },
5851
5852     /**
5853      * Used to define events on this Observable
5854      * @param {Object} object The object with the events defined
5855      */
5856     addEvents : function(o){
5857         if(!this.events){
5858             this.events = {};
5859         }
5860         Roo.applyIf(this.events, o);
5861     },
5862
5863     /**
5864      * Checks to see if this object has any listeners for a specified event
5865      * @param {String} eventName The name of the event to check for
5866      * @return {Boolean} True if the event is being listened for, else false
5867      */
5868     hasListener : function(eventName){
5869         var e = this.events[eventName];
5870         return typeof e == "object" && e.listeners.length > 0;
5871     }
5872 };
5873 /**
5874  * Appends an event handler to this element (shorthand for addListener)
5875  * @param {String}   eventName     The type of event to listen for
5876  * @param {Function} handler        The method the event invokes
5877  * @param {Object}   scope (optional) The scope in which to execute the handler
5878  * function. The handler function's "this" context.
5879  * @param {Object}   options  (optional)
5880  * @method
5881  */
5882 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5883 /**
5884  * Removes a listener (shorthand for removeListener)
5885  * @param {String}   eventName     The type of event to listen for
5886  * @param {Function} handler        The handler to remove
5887  * @param {Object}   scope  (optional) The scope (this object) for the handler
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5891
5892 /**
5893  * Starts capture on the specified Observable. All events will be passed
5894  * to the supplied function with the event name + standard signature of the event
5895  * <b>before</b> the event is fired. If the supplied function returns false,
5896  * the event will not fire.
5897  * @param {Observable} o The Observable to capture
5898  * @param {Function} fn The function to call
5899  * @param {Object} scope (optional) The scope (this object) for the fn
5900  * @static
5901  */
5902 Roo.util.Observable.capture = function(o, fn, scope){
5903     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5904 };
5905
5906 /**
5907  * Removes <b>all</b> added captures from the Observable.
5908  * @param {Observable} o The Observable to release
5909  * @static
5910  */
5911 Roo.util.Observable.releaseCapture = function(o){
5912     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5913 };
5914
5915 (function(){
5916
5917     var createBuffered = function(h, o, scope){
5918         var task = new Roo.util.DelayedTask();
5919         return function(){
5920             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5921         };
5922     };
5923
5924     var createSingle = function(h, e, fn, scope){
5925         return function(){
5926             e.removeListener(fn, scope);
5927             return h.apply(scope, arguments);
5928         };
5929     };
5930
5931     var createDelayed = function(h, o, scope){
5932         return function(){
5933             var args = Array.prototype.slice.call(arguments, 0);
5934             setTimeout(function(){
5935                 h.apply(scope, args);
5936             }, o.delay || 10);
5937         };
5938     };
5939
5940     Roo.util.Event = function(obj, name){
5941         this.name = name;
5942         this.obj = obj;
5943         this.listeners = [];
5944     };
5945
5946     Roo.util.Event.prototype = {
5947         addListener : function(fn, scope, options){
5948             var o = options || {};
5949             scope = scope || this.obj;
5950             if(!this.isListening(fn, scope)){
5951                 var l = {fn: fn, scope: scope, options: o};
5952                 var h = fn;
5953                 if(o.delay){
5954                     h = createDelayed(h, o, scope);
5955                 }
5956                 if(o.single){
5957                     h = createSingle(h, this, fn, scope);
5958                 }
5959                 if(o.buffer){
5960                     h = createBuffered(h, o, scope);
5961                 }
5962                 l.fireFn = h;
5963                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5964                     this.listeners.push(l);
5965                 }else{
5966                     this.listeners = this.listeners.slice(0);
5967                     this.listeners.push(l);
5968                 }
5969             }
5970         },
5971
5972         findListener : function(fn, scope){
5973             scope = scope || this.obj;
5974             var ls = this.listeners;
5975             for(var i = 0, len = ls.length; i < len; i++){
5976                 var l = ls[i];
5977                 if(l.fn == fn && l.scope == scope){
5978                     return i;
5979                 }
5980             }
5981             return -1;
5982         },
5983
5984         isListening : function(fn, scope){
5985             return this.findListener(fn, scope) != -1;
5986         },
5987
5988         removeListener : function(fn, scope){
5989             var index;
5990             if((index = this.findListener(fn, scope)) != -1){
5991                 if(!this.firing){
5992                     this.listeners.splice(index, 1);
5993                 }else{
5994                     this.listeners = this.listeners.slice(0);
5995                     this.listeners.splice(index, 1);
5996                 }
5997                 return true;
5998             }
5999             return false;
6000         },
6001
6002         clearListeners : function(){
6003             this.listeners = [];
6004         },
6005
6006         fire : function(){
6007             var ls = this.listeners, scope, len = ls.length;
6008             if(len > 0){
6009                 this.firing = true;
6010                 var args = Array.prototype.slice.call(arguments, 0);
6011                 for(var i = 0; i < len; i++){
6012                     var l = ls[i];
6013                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6014                         this.firing = false;
6015                         return false;
6016                     }
6017                 }
6018                 this.firing = false;
6019             }
6020             return true;
6021         }
6022     };
6023 })();/*
6024  * Based on:
6025  * Ext JS Library 1.1.1
6026  * Copyright(c) 2006-2007, Ext JS, LLC.
6027  *
6028  * Originally Released Under LGPL - original licence link has changed is not relivant.
6029  *
6030  * Fork - LGPL
6031  * <script type="text/javascript">
6032  */
6033
6034 /**
6035  * @class Roo.EventManager
6036  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6037  * several useful events directly.
6038  * See {@link Roo.EventObject} for more details on normalized event objects.
6039  * @singleton
6040  */
6041 Roo.EventManager = function(){
6042     var docReadyEvent, docReadyProcId, docReadyState = false;
6043     var resizeEvent, resizeTask, textEvent, textSize;
6044     var E = Roo.lib.Event;
6045     var D = Roo.lib.Dom;
6046
6047
6048     var fireDocReady = function(){
6049         if(!docReadyState){
6050             docReadyState = true;
6051             Roo.isReady = true;
6052             if(docReadyProcId){
6053                 clearInterval(docReadyProcId);
6054             }
6055             if(Roo.isGecko || Roo.isOpera) {
6056                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6057             }
6058             if(Roo.isIE){
6059                 var defer = document.getElementById("ie-deferred-loader");
6060                 if(defer){
6061                     defer.onreadystatechange = null;
6062                     defer.parentNode.removeChild(defer);
6063                 }
6064             }
6065             if(docReadyEvent){
6066                 docReadyEvent.fire();
6067                 docReadyEvent.clearListeners();
6068             }
6069         }
6070     };
6071     
6072     var initDocReady = function(){
6073         docReadyEvent = new Roo.util.Event();
6074         if(Roo.isGecko || Roo.isOpera) {
6075             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6076         }else if(Roo.isIE){
6077             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6078             var defer = document.getElementById("ie-deferred-loader");
6079             defer.onreadystatechange = function(){
6080                 if(this.readyState == "complete"){
6081                     fireDocReady();
6082                 }
6083             };
6084         }else if(Roo.isSafari){ 
6085             docReadyProcId = setInterval(function(){
6086                 var rs = document.readyState;
6087                 if(rs == "complete") {
6088                     fireDocReady();     
6089                  }
6090             }, 10);
6091         }
6092         // no matter what, make sure it fires on load
6093         E.on(window, "load", fireDocReady);
6094     };
6095
6096     var createBuffered = function(h, o){
6097         var task = new Roo.util.DelayedTask(h);
6098         return function(e){
6099             // create new event object impl so new events don't wipe out properties
6100             e = new Roo.EventObjectImpl(e);
6101             task.delay(o.buffer, h, null, [e]);
6102         };
6103     };
6104
6105     var createSingle = function(h, el, ename, fn){
6106         return function(e){
6107             Roo.EventManager.removeListener(el, ename, fn);
6108             h(e);
6109         };
6110     };
6111
6112     var createDelayed = function(h, o){
6113         return function(e){
6114             // create new event object impl so new events don't wipe out properties
6115             e = new Roo.EventObjectImpl(e);
6116             setTimeout(function(){
6117                 h(e);
6118             }, o.delay || 10);
6119         };
6120     };
6121
6122     var listen = function(element, ename, opt, fn, scope){
6123         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6124         fn = fn || o.fn; scope = scope || o.scope;
6125         var el = Roo.getDom(element);
6126         if(!el){
6127             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6128         }
6129         var h = function(e){
6130             e = Roo.EventObject.setEvent(e);
6131             var t;
6132             if(o.delegate){
6133                 t = e.getTarget(o.delegate, el);
6134                 if(!t){
6135                     return;
6136                 }
6137             }else{
6138                 t = e.target;
6139             }
6140             if(o.stopEvent === true){
6141                 e.stopEvent();
6142             }
6143             if(o.preventDefault === true){
6144                e.preventDefault();
6145             }
6146             if(o.stopPropagation === true){
6147                 e.stopPropagation();
6148             }
6149
6150             if(o.normalized === false){
6151                 e = e.browserEvent;
6152             }
6153
6154             fn.call(scope || el, e, t, o);
6155         };
6156         if(o.delay){
6157             h = createDelayed(h, o);
6158         }
6159         if(o.single){
6160             h = createSingle(h, el, ename, fn);
6161         }
6162         if(o.buffer){
6163             h = createBuffered(h, o);
6164         }
6165         fn._handlers = fn._handlers || [];
6166         fn._handlers.push([Roo.id(el), ename, h]);
6167
6168         E.on(el, ename, h);
6169         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6170             el.addEventListener("DOMMouseScroll", h, false);
6171             E.on(window, 'unload', function(){
6172                 el.removeEventListener("DOMMouseScroll", h, false);
6173             });
6174         }
6175         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6176             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6177         }
6178         return h;
6179     };
6180
6181     var stopListening = function(el, ename, fn){
6182         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6183         if(hds){
6184             for(var i = 0, len = hds.length; i < len; i++){
6185                 var h = hds[i];
6186                 if(h[0] == id && h[1] == ename){
6187                     hd = h[2];
6188                     hds.splice(i, 1);
6189                     break;
6190                 }
6191             }
6192         }
6193         E.un(el, ename, hd);
6194         el = Roo.getDom(el);
6195         if(ename == "mousewheel" && el.addEventListener){
6196             el.removeEventListener("DOMMouseScroll", hd, false);
6197         }
6198         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6199             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6200         }
6201     };
6202
6203     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6204     
6205     var pub = {
6206         
6207         
6208         /** 
6209          * Fix for doc tools
6210          * @scope Roo.EventManager
6211          */
6212         
6213         
6214         /** 
6215          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6216          * object with a Roo.EventObject
6217          * @param {Function} fn        The method the event invokes
6218          * @param {Object}   scope    An object that becomes the scope of the handler
6219          * @param {boolean}  override If true, the obj passed in becomes
6220          *                             the execution scope of the listener
6221          * @return {Function} The wrapped function
6222          * @deprecated
6223          */
6224         wrap : function(fn, scope, override){
6225             return function(e){
6226                 Roo.EventObject.setEvent(e);
6227                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6228             };
6229         },
6230         
6231         /**
6232      * Appends an event handler to an element (shorthand for addListener)
6233      * @param {String/HTMLElement}   element        The html element or id to assign the
6234      * @param {String}   eventName The type of event to listen for
6235      * @param {Function} handler The method the event invokes
6236      * @param {Object}   scope (optional) The scope in which to execute the handler
6237      * function. The handler function's "this" context.
6238      * @param {Object}   options (optional) An object containing handler configuration
6239      * properties. This may contain any of the following properties:<ul>
6240      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6241      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6242      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6243      * <li>preventDefault {Boolean} True to prevent the default action</li>
6244      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6245      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6246      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6247      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6248      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6249      * by the specified number of milliseconds. If the event fires again within that time, the original
6250      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6251      * </ul><br>
6252      * <p>
6253      * <b>Combining Options</b><br>
6254      * Using the options argument, it is possible to combine different types of listeners:<br>
6255      * <br>
6256      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6257      * Code:<pre><code>
6258 el.on('click', this.onClick, this, {
6259     single: true,
6260     delay: 100,
6261     stopEvent : true,
6262     forumId: 4
6263 });</code></pre>
6264      * <p>
6265      * <b>Attaching multiple handlers in 1 call</b><br>
6266       * The method also allows for a single argument to be passed which is a config object containing properties
6267      * which specify multiple handlers.
6268      * <p>
6269      * Code:<pre><code>
6270 el.on({
6271     'click' : {
6272         fn: this.onClick
6273         scope: this,
6274         delay: 100
6275     },
6276     'mouseover' : {
6277         fn: this.onMouseOver
6278         scope: this
6279     },
6280     'mouseout' : {
6281         fn: this.onMouseOut
6282         scope: this
6283     }
6284 });</code></pre>
6285      * <p>
6286      * Or a shorthand syntax:<br>
6287      * Code:<pre><code>
6288 el.on({
6289     'click' : this.onClick,
6290     'mouseover' : this.onMouseOver,
6291     'mouseout' : this.onMouseOut
6292     scope: this
6293 });</code></pre>
6294      */
6295         addListener : function(element, eventName, fn, scope, options){
6296             if(typeof eventName == "object"){
6297                 var o = eventName;
6298                 for(var e in o){
6299                     if(propRe.test(e)){
6300                         continue;
6301                     }
6302                     if(typeof o[e] == "function"){
6303                         // shared options
6304                         listen(element, e, o, o[e], o.scope);
6305                     }else{
6306                         // individual options
6307                         listen(element, e, o[e]);
6308                     }
6309                 }
6310                 return;
6311             }
6312             return listen(element, eventName, options, fn, scope);
6313         },
6314         
6315         /**
6316          * Removes an event handler
6317          *
6318          * @param {String/HTMLElement}   element        The id or html element to remove the 
6319          *                             event from
6320          * @param {String}   eventName     The type of event
6321          * @param {Function} fn
6322          * @return {Boolean} True if a listener was actually removed
6323          */
6324         removeListener : function(element, eventName, fn){
6325             return stopListening(element, eventName, fn);
6326         },
6327         
6328         /**
6329          * Fires when the document is ready (before onload and before images are loaded). Can be 
6330          * accessed shorthanded Roo.onReady().
6331          * @param {Function} fn        The method the event invokes
6332          * @param {Object}   scope    An  object that becomes the scope of the handler
6333          * @param {boolean}  options
6334          */
6335         onDocumentReady : function(fn, scope, options){
6336             if(docReadyState){ // if it already fired
6337                 docReadyEvent.addListener(fn, scope, options);
6338                 docReadyEvent.fire();
6339                 docReadyEvent.clearListeners();
6340                 return;
6341             }
6342             if(!docReadyEvent){
6343                 initDocReady();
6344             }
6345             docReadyEvent.addListener(fn, scope, options);
6346         },
6347         
6348         /**
6349          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6350          * @param {Function} fn        The method the event invokes
6351          * @param {Object}   scope    An object that becomes the scope of the handler
6352          * @param {boolean}  options
6353          */
6354         onWindowResize : function(fn, scope, options){
6355             if(!resizeEvent){
6356                 resizeEvent = new Roo.util.Event();
6357                 resizeTask = new Roo.util.DelayedTask(function(){
6358                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6359                 });
6360                 E.on(window, "resize", function(){
6361                     if(Roo.isIE){
6362                         resizeTask.delay(50);
6363                     }else{
6364                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6365                     }
6366                 });
6367             }
6368             resizeEvent.addListener(fn, scope, options);
6369         },
6370
6371         /**
6372          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6373          * @param {Function} fn        The method the event invokes
6374          * @param {Object}   scope    An object that becomes the scope of the handler
6375          * @param {boolean}  options
6376          */
6377         onTextResize : function(fn, scope, options){
6378             if(!textEvent){
6379                 textEvent = new Roo.util.Event();
6380                 var textEl = new Roo.Element(document.createElement('div'));
6381                 textEl.dom.className = 'x-text-resize';
6382                 textEl.dom.innerHTML = 'X';
6383                 textEl.appendTo(document.body);
6384                 textSize = textEl.dom.offsetHeight;
6385                 setInterval(function(){
6386                     if(textEl.dom.offsetHeight != textSize){
6387                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6388                     }
6389                 }, this.textResizeInterval);
6390             }
6391             textEvent.addListener(fn, scope, options);
6392         },
6393
6394         /**
6395          * Removes the passed window resize listener.
6396          * @param {Function} fn        The method the event invokes
6397          * @param {Object}   scope    The scope of handler
6398          */
6399         removeResizeListener : function(fn, scope){
6400             if(resizeEvent){
6401                 resizeEvent.removeListener(fn, scope);
6402             }
6403         },
6404
6405         // private
6406         fireResize : function(){
6407             if(resizeEvent){
6408                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409             }   
6410         },
6411         /**
6412          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6413          */
6414         ieDeferSrc : false,
6415         /**
6416          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6417          */
6418         textResizeInterval : 50
6419     };
6420     
6421     /**
6422      * Fix for doc tools
6423      * @scopeAlias pub=Roo.EventManager
6424      */
6425     
6426      /**
6427      * Appends an event handler to an element (shorthand for addListener)
6428      * @param {String/HTMLElement}   element        The html element or id to assign the
6429      * @param {String}   eventName The type of event to listen for
6430      * @param {Function} handler The method the event invokes
6431      * @param {Object}   scope (optional) The scope in which to execute the handler
6432      * function. The handler function's "this" context.
6433      * @param {Object}   options (optional) An object containing handler configuration
6434      * properties. This may contain any of the following properties:<ul>
6435      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6436      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6437      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6438      * <li>preventDefault {Boolean} True to prevent the default action</li>
6439      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6440      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6441      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6442      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6443      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6444      * by the specified number of milliseconds. If the event fires again within that time, the original
6445      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6446      * </ul><br>
6447      * <p>
6448      * <b>Combining Options</b><br>
6449      * Using the options argument, it is possible to combine different types of listeners:<br>
6450      * <br>
6451      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6452      * Code:<pre><code>
6453 el.on('click', this.onClick, this, {
6454     single: true,
6455     delay: 100,
6456     stopEvent : true,
6457     forumId: 4
6458 });</code></pre>
6459      * <p>
6460      * <b>Attaching multiple handlers in 1 call</b><br>
6461       * The method also allows for a single argument to be passed which is a config object containing properties
6462      * which specify multiple handlers.
6463      * <p>
6464      * Code:<pre><code>
6465 el.on({
6466     'click' : {
6467         fn: this.onClick
6468         scope: this,
6469         delay: 100
6470     },
6471     'mouseover' : {
6472         fn: this.onMouseOver
6473         scope: this
6474     },
6475     'mouseout' : {
6476         fn: this.onMouseOut
6477         scope: this
6478     }
6479 });</code></pre>
6480      * <p>
6481      * Or a shorthand syntax:<br>
6482      * Code:<pre><code>
6483 el.on({
6484     'click' : this.onClick,
6485     'mouseover' : this.onMouseOver,
6486     'mouseout' : this.onMouseOut
6487     scope: this
6488 });</code></pre>
6489      */
6490     pub.on = pub.addListener;
6491     pub.un = pub.removeListener;
6492
6493     pub.stoppedMouseDownEvent = new Roo.util.Event();
6494     return pub;
6495 }();
6496 /**
6497   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6498   * @param {Function} fn        The method the event invokes
6499   * @param {Object}   scope    An  object that becomes the scope of the handler
6500   * @param {boolean}  override If true, the obj passed in becomes
6501   *                             the execution scope of the listener
6502   * @member Roo
6503   * @method onReady
6504  */
6505 Roo.onReady = Roo.EventManager.onDocumentReady;
6506
6507 Roo.onReady(function(){
6508     var bd = Roo.get(document.body);
6509     if(!bd){ return; }
6510
6511     var cls = [
6512             Roo.isIE ? "roo-ie"
6513             : Roo.isGecko ? "roo-gecko"
6514             : Roo.isOpera ? "roo-opera"
6515             : Roo.isSafari ? "roo-safari" : ""];
6516
6517     if(Roo.isMac){
6518         cls.push("roo-mac");
6519     }
6520     if(Roo.isLinux){
6521         cls.push("roo-linux");
6522     }
6523     if(Roo.isBorderBox){
6524         cls.push('roo-border-box');
6525     }
6526     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6527         var p = bd.dom.parentNode;
6528         if(p){
6529             p.className += ' roo-strict';
6530         }
6531     }
6532     bd.addClass(cls.join(' '));
6533 });
6534
6535 /**
6536  * @class Roo.EventObject
6537  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6538  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6539  * Example:
6540  * <pre><code>
6541  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6542     e.preventDefault();
6543     var target = e.getTarget();
6544     ...
6545  }
6546  var myDiv = Roo.get("myDiv");
6547  myDiv.on("click", handleClick);
6548  //or
6549  Roo.EventManager.on("myDiv", 'click', handleClick);
6550  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6551  </code></pre>
6552  * @singleton
6553  */
6554 Roo.EventObject = function(){
6555     
6556     var E = Roo.lib.Event;
6557     
6558     // safari keypress events for special keys return bad keycodes
6559     var safariKeys = {
6560         63234 : 37, // left
6561         63235 : 39, // right
6562         63232 : 38, // up
6563         63233 : 40, // down
6564         63276 : 33, // page up
6565         63277 : 34, // page down
6566         63272 : 46, // delete
6567         63273 : 36, // home
6568         63275 : 35  // end
6569     };
6570
6571     // normalize button clicks
6572     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6573                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6574
6575     Roo.EventObjectImpl = function(e){
6576         if(e){
6577             this.setEvent(e.browserEvent || e);
6578         }
6579     };
6580     Roo.EventObjectImpl.prototype = {
6581         /**
6582          * Used to fix doc tools.
6583          * @scope Roo.EventObject.prototype
6584          */
6585             
6586
6587         
6588         
6589         /** The normal browser event */
6590         browserEvent : null,
6591         /** The button pressed in a mouse event */
6592         button : -1,
6593         /** True if the shift key was down during the event */
6594         shiftKey : false,
6595         /** True if the control key was down during the event */
6596         ctrlKey : false,
6597         /** True if the alt key was down during the event */
6598         altKey : false,
6599
6600         /** Key constant 
6601         * @type Number */
6602         BACKSPACE : 8,
6603         /** Key constant 
6604         * @type Number */
6605         TAB : 9,
6606         /** Key constant 
6607         * @type Number */
6608         RETURN : 13,
6609         /** Key constant 
6610         * @type Number */
6611         ENTER : 13,
6612         /** Key constant 
6613         * @type Number */
6614         SHIFT : 16,
6615         /** Key constant 
6616         * @type Number */
6617         CONTROL : 17,
6618         /** Key constant 
6619         * @type Number */
6620         ESC : 27,
6621         /** Key constant 
6622         * @type Number */
6623         SPACE : 32,
6624         /** Key constant 
6625         * @type Number */
6626         PAGEUP : 33,
6627         /** Key constant 
6628         * @type Number */
6629         PAGEDOWN : 34,
6630         /** Key constant 
6631         * @type Number */
6632         END : 35,
6633         /** Key constant 
6634         * @type Number */
6635         HOME : 36,
6636         /** Key constant 
6637         * @type Number */
6638         LEFT : 37,
6639         /** Key constant 
6640         * @type Number */
6641         UP : 38,
6642         /** Key constant 
6643         * @type Number */
6644         RIGHT : 39,
6645         /** Key constant 
6646         * @type Number */
6647         DOWN : 40,
6648         /** Key constant 
6649         * @type Number */
6650         DELETE : 46,
6651         /** Key constant 
6652         * @type Number */
6653         F5 : 116,
6654
6655            /** @private */
6656         setEvent : function(e){
6657             if(e == this || (e && e.browserEvent)){ // already wrapped
6658                 return e;
6659             }
6660             this.browserEvent = e;
6661             if(e){
6662                 // normalize buttons
6663                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6664                 if(e.type == 'click' && this.button == -1){
6665                     this.button = 0;
6666                 }
6667                 this.type = e.type;
6668                 this.shiftKey = e.shiftKey;
6669                 // mac metaKey behaves like ctrlKey
6670                 this.ctrlKey = e.ctrlKey || e.metaKey;
6671                 this.altKey = e.altKey;
6672                 // in getKey these will be normalized for the mac
6673                 this.keyCode = e.keyCode;
6674                 // keyup warnings on firefox.
6675                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6676                 // cache the target for the delayed and or buffered events
6677                 this.target = E.getTarget(e);
6678                 // same for XY
6679                 this.xy = E.getXY(e);
6680             }else{
6681                 this.button = -1;
6682                 this.shiftKey = false;
6683                 this.ctrlKey = false;
6684                 this.altKey = false;
6685                 this.keyCode = 0;
6686                 this.charCode =0;
6687                 this.target = null;
6688                 this.xy = [0, 0];
6689             }
6690             return this;
6691         },
6692
6693         /**
6694          * Stop the event (preventDefault and stopPropagation)
6695          */
6696         stopEvent : function(){
6697             if(this.browserEvent){
6698                 if(this.browserEvent.type == 'mousedown'){
6699                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6700                 }
6701                 E.stopEvent(this.browserEvent);
6702             }
6703         },
6704
6705         /**
6706          * Prevents the browsers default handling of the event.
6707          */
6708         preventDefault : function(){
6709             if(this.browserEvent){
6710                 E.preventDefault(this.browserEvent);
6711             }
6712         },
6713
6714         /** @private */
6715         isNavKeyPress : function(){
6716             var k = this.keyCode;
6717             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6718             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6719         },
6720
6721         isSpecialKey : function(){
6722             var k = this.keyCode;
6723             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6724             (k == 16) || (k == 17) ||
6725             (k >= 18 && k <= 20) ||
6726             (k >= 33 && k <= 35) ||
6727             (k >= 36 && k <= 39) ||
6728             (k >= 44 && k <= 45);
6729         },
6730         /**
6731          * Cancels bubbling of the event.
6732          */
6733         stopPropagation : function(){
6734             if(this.browserEvent){
6735                 if(this.type == 'mousedown'){
6736                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6737                 }
6738                 E.stopPropagation(this.browserEvent);
6739             }
6740         },
6741
6742         /**
6743          * Gets the key code for the event.
6744          * @return {Number}
6745          */
6746         getCharCode : function(){
6747             return this.charCode || this.keyCode;
6748         },
6749
6750         /**
6751          * Returns a normalized keyCode for the event.
6752          * @return {Number} The key code
6753          */
6754         getKey : function(){
6755             var k = this.keyCode || this.charCode;
6756             return Roo.isSafari ? (safariKeys[k] || k) : k;
6757         },
6758
6759         /**
6760          * Gets the x coordinate of the event.
6761          * @return {Number}
6762          */
6763         getPageX : function(){
6764             return this.xy[0];
6765         },
6766
6767         /**
6768          * Gets the y coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageY : function(){
6772             return this.xy[1];
6773         },
6774
6775         /**
6776          * Gets the time of the event.
6777          * @return {Number}
6778          */
6779         getTime : function(){
6780             if(this.browserEvent){
6781                 return E.getTime(this.browserEvent);
6782             }
6783             return null;
6784         },
6785
6786         /**
6787          * Gets the page coordinates of the event.
6788          * @return {Array} The xy values like [x, y]
6789          */
6790         getXY : function(){
6791             return this.xy;
6792         },
6793
6794         /**
6795          * Gets the target for the event.
6796          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6797          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6798                 search as a number or element (defaults to 10 || document.body)
6799          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6800          * @return {HTMLelement}
6801          */
6802         getTarget : function(selector, maxDepth, returnEl){
6803             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6804         },
6805         /**
6806          * Gets the related target.
6807          * @return {HTMLElement}
6808          */
6809         getRelatedTarget : function(){
6810             if(this.browserEvent){
6811                 return E.getRelatedTarget(this.browserEvent);
6812             }
6813             return null;
6814         },
6815
6816         /**
6817          * Normalizes mouse wheel delta across browsers
6818          * @return {Number} The delta
6819          */
6820         getWheelDelta : function(){
6821             var e = this.browserEvent;
6822             var delta = 0;
6823             if(e.wheelDelta){ /* IE/Opera. */
6824                 delta = e.wheelDelta/120;
6825             }else if(e.detail){ /* Mozilla case. */
6826                 delta = -e.detail/3;
6827             }
6828             return delta;
6829         },
6830
6831         /**
6832          * Returns true if the control, meta, shift or alt key was pressed during this event.
6833          * @return {Boolean}
6834          */
6835         hasModifier : function(){
6836             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6837         },
6838
6839         /**
6840          * Returns true if the target of this event equals el or is a child of el
6841          * @param {String/HTMLElement/Element} el
6842          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6843          * @return {Boolean}
6844          */
6845         within : function(el, related){
6846             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6847             return t && Roo.fly(el).contains(t);
6848         },
6849
6850         getPoint : function(){
6851             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6852         }
6853     };
6854
6855     return new Roo.EventObjectImpl();
6856 }();
6857             
6858     /*
6859  * Based on:
6860  * Ext JS Library 1.1.1
6861  * Copyright(c) 2006-2007, Ext JS, LLC.
6862  *
6863  * Originally Released Under LGPL - original licence link has changed is not relivant.
6864  *
6865  * Fork - LGPL
6866  * <script type="text/javascript">
6867  */
6868
6869  
6870 // was in Composite Element!??!?!
6871  
6872 (function(){
6873     var D = Roo.lib.Dom;
6874     var E = Roo.lib.Event;
6875     var A = Roo.lib.Anim;
6876
6877     // local style camelizing for speed
6878     var propCache = {};
6879     var camelRe = /(-[a-z])/gi;
6880     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6881     var view = document.defaultView;
6882
6883 /**
6884  * @class Roo.Element
6885  * Represents an Element in the DOM.<br><br>
6886  * Usage:<br>
6887 <pre><code>
6888 var el = Roo.get("my-div");
6889
6890 // or with getEl
6891 var el = getEl("my-div");
6892
6893 // or with a DOM element
6894 var el = Roo.get(myDivElement);
6895 </code></pre>
6896  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6897  * each call instead of constructing a new one.<br><br>
6898  * <b>Animations</b><br />
6899  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6900  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6901 <pre>
6902 Option    Default   Description
6903 --------- --------  ---------------------------------------------
6904 duration  .35       The duration of the animation in seconds
6905 easing    easeOut   The YUI easing method
6906 callback  none      A function to execute when the anim completes
6907 scope     this      The scope (this) of the callback function
6908 </pre>
6909 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6910 * manipulate the animation. Here's an example:
6911 <pre><code>
6912 var el = Roo.get("my-div");
6913
6914 // no animation
6915 el.setWidth(100);
6916
6917 // default animation
6918 el.setWidth(100, true);
6919
6920 // animation with some options set
6921 el.setWidth(100, {
6922     duration: 1,
6923     callback: this.foo,
6924     scope: this
6925 });
6926
6927 // using the "anim" property to get the Anim object
6928 var opt = {
6929     duration: 1,
6930     callback: this.foo,
6931     scope: this
6932 };
6933 el.setWidth(100, opt);
6934 ...
6935 if(opt.anim.isAnimated()){
6936     opt.anim.stop();
6937 }
6938 </code></pre>
6939 * <b> Composite (Collections of) Elements</b><br />
6940  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6941  * @constructor Create a new Element directly.
6942  * @param {String/HTMLElement} element
6943  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6944  */
6945     Roo.Element = function(element, forceNew){
6946         var dom = typeof element == "string" ?
6947                 document.getElementById(element) : element;
6948         if(!dom){ // invalid id/element
6949             return null;
6950         }
6951         var id = dom.id;
6952         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6953             return Roo.Element.cache[id];
6954         }
6955
6956         /**
6957          * The DOM element
6958          * @type HTMLElement
6959          */
6960         this.dom = dom;
6961
6962         /**
6963          * The DOM element ID
6964          * @type String
6965          */
6966         this.id = id || Roo.id(dom);
6967     };
6968
6969     var El = Roo.Element;
6970
6971     El.prototype = {
6972         /**
6973          * The element's default display mode  (defaults to "")
6974          * @type String
6975          */
6976         originalDisplay : "",
6977
6978         visibilityMode : 1,
6979         /**
6980          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6981          * @type String
6982          */
6983         defaultUnit : "px",
6984         /**
6985          * Sets the element's visibility mode. When setVisible() is called it
6986          * will use this to determine whether to set the visibility or the display property.
6987          * @param visMode Element.VISIBILITY or Element.DISPLAY
6988          * @return {Roo.Element} this
6989          */
6990         setVisibilityMode : function(visMode){
6991             this.visibilityMode = visMode;
6992             return this;
6993         },
6994         /**
6995          * Convenience method for setVisibilityMode(Element.DISPLAY)
6996          * @param {String} display (optional) What to set display to when visible
6997          * @return {Roo.Element} this
6998          */
6999         enableDisplayMode : function(display){
7000             this.setVisibilityMode(El.DISPLAY);
7001             if(typeof display != "undefined") this.originalDisplay = display;
7002             return this;
7003         },
7004
7005         /**
7006          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7007          * @param {String} selector The simple selector to test
7008          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7009                 search as a number or element (defaults to 10 || document.body)
7010          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7011          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7012          */
7013         findParent : function(simpleSelector, maxDepth, returnEl){
7014             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7015             maxDepth = maxDepth || 50;
7016             if(typeof maxDepth != "number"){
7017                 stopEl = Roo.getDom(maxDepth);
7018                 maxDepth = 10;
7019             }
7020             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7021                 if(dq.is(p, simpleSelector)){
7022                     return returnEl ? Roo.get(p) : p;
7023                 }
7024                 depth++;
7025                 p = p.parentNode;
7026             }
7027             return null;
7028         },
7029
7030
7031         /**
7032          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7033          * @param {String} selector The simple selector to test
7034          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7035                 search as a number or element (defaults to 10 || document.body)
7036          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7037          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7038          */
7039         findParentNode : function(simpleSelector, maxDepth, returnEl){
7040             var p = Roo.fly(this.dom.parentNode, '_internal');
7041             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7042         },
7043
7044         /**
7045          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7046          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7047          * @param {String} selector The simple selector to test
7048          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7049                 search as a number or element (defaults to 10 || document.body)
7050          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7051          */
7052         up : function(simpleSelector, maxDepth){
7053             return this.findParentNode(simpleSelector, maxDepth, true);
7054         },
7055
7056
7057
7058         /**
7059          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7060          * @param {String} selector The simple selector to test
7061          * @return {Boolean} True if this element matches the selector, else false
7062          */
7063         is : function(simpleSelector){
7064             return Roo.DomQuery.is(this.dom, simpleSelector);
7065         },
7066
7067         /**
7068          * Perform animation on this element.
7069          * @param {Object} args The YUI animation control args
7070          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7071          * @param {Function} onComplete (optional) Function to call when animation completes
7072          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7073          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7074          * @return {Roo.Element} this
7075          */
7076         animate : function(args, duration, onComplete, easing, animType){
7077             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7078             return this;
7079         },
7080
7081         /*
7082          * @private Internal animation call
7083          */
7084         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7085             animType = animType || 'run';
7086             opt = opt || {};
7087             var anim = Roo.lib.Anim[animType](
7088                 this.dom, args,
7089                 (opt.duration || defaultDur) || .35,
7090                 (opt.easing || defaultEase) || 'easeOut',
7091                 function(){
7092                     Roo.callback(cb, this);
7093                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7094                 },
7095                 this
7096             );
7097             opt.anim = anim;
7098             return anim;
7099         },
7100
7101         // private legacy anim prep
7102         preanim : function(a, i){
7103             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7104         },
7105
7106         /**
7107          * Removes worthless text nodes
7108          * @param {Boolean} forceReclean (optional) By default the element
7109          * keeps track if it has been cleaned already so
7110          * you can call this over and over. However, if you update the element and
7111          * need to force a reclean, you can pass true.
7112          */
7113         clean : function(forceReclean){
7114             if(this.isCleaned && forceReclean !== true){
7115                 return this;
7116             }
7117             var ns = /\S/;
7118             var d = this.dom, n = d.firstChild, ni = -1;
7119             while(n){
7120                 var nx = n.nextSibling;
7121                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7122                     d.removeChild(n);
7123                 }else{
7124                     n.nodeIndex = ++ni;
7125                 }
7126                 n = nx;
7127             }
7128             this.isCleaned = true;
7129             return this;
7130         },
7131
7132         // private
7133         calcOffsetsTo : function(el){
7134             el = Roo.get(el);
7135             var d = el.dom;
7136             var restorePos = false;
7137             if(el.getStyle('position') == 'static'){
7138                 el.position('relative');
7139                 restorePos = true;
7140             }
7141             var x = 0, y =0;
7142             var op = this.dom;
7143             while(op && op != d && op.tagName != 'HTML'){
7144                 x+= op.offsetLeft;
7145                 y+= op.offsetTop;
7146                 op = op.offsetParent;
7147             }
7148             if(restorePos){
7149                 el.position('static');
7150             }
7151             return [x, y];
7152         },
7153
7154         /**
7155          * Scrolls this element into view within the passed container.
7156          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7157          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7158          * @return {Roo.Element} this
7159          */
7160         scrollIntoView : function(container, hscroll){
7161             var c = Roo.getDom(container) || document.body;
7162             var el = this.dom;
7163
7164             var o = this.calcOffsetsTo(c),
7165                 l = o[0],
7166                 t = o[1],
7167                 b = t+el.offsetHeight,
7168                 r = l+el.offsetWidth;
7169
7170             var ch = c.clientHeight;
7171             var ct = parseInt(c.scrollTop, 10);
7172             var cl = parseInt(c.scrollLeft, 10);
7173             var cb = ct + ch;
7174             var cr = cl + c.clientWidth;
7175
7176             if(t < ct){
7177                 c.scrollTop = t;
7178             }else if(b > cb){
7179                 c.scrollTop = b-ch;
7180             }
7181
7182             if(hscroll !== false){
7183                 if(l < cl){
7184                     c.scrollLeft = l;
7185                 }else if(r > cr){
7186                     c.scrollLeft = r-c.clientWidth;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         // private
7193         scrollChildIntoView : function(child, hscroll){
7194             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7195         },
7196
7197         /**
7198          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7199          * the new height may not be available immediately.
7200          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7201          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7202          * @param {Function} onComplete (optional) Function to call when animation completes
7203          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7204          * @return {Roo.Element} this
7205          */
7206         autoHeight : function(animate, duration, onComplete, easing){
7207             var oldHeight = this.getHeight();
7208             this.clip();
7209             this.setHeight(1); // force clipping
7210             setTimeout(function(){
7211                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7212                 if(!animate){
7213                     this.setHeight(height);
7214                     this.unclip();
7215                     if(typeof onComplete == "function"){
7216                         onComplete();
7217                     }
7218                 }else{
7219                     this.setHeight(oldHeight); // restore original height
7220                     this.setHeight(height, animate, duration, function(){
7221                         this.unclip();
7222                         if(typeof onComplete == "function") onComplete();
7223                     }.createDelegate(this), easing);
7224                 }
7225             }.createDelegate(this), 0);
7226             return this;
7227         },
7228
7229         /**
7230          * Returns true if this element is an ancestor of the passed element
7231          * @param {HTMLElement/String} el The element to check
7232          * @return {Boolean} True if this element is an ancestor of el, else false
7233          */
7234         contains : function(el){
7235             if(!el){return false;}
7236             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7237         },
7238
7239         /**
7240          * Checks whether the element is currently visible using both visibility and display properties.
7241          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7242          * @return {Boolean} True if the element is currently visible, else false
7243          */
7244         isVisible : function(deep) {
7245             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7246             if(deep !== true || !vis){
7247                 return vis;
7248             }
7249             var p = this.dom.parentNode;
7250             while(p && p.tagName.toLowerCase() != "body"){
7251                 if(!Roo.fly(p, '_isVisible').isVisible()){
7252                     return false;
7253                 }
7254                 p = p.parentNode;
7255             }
7256             return true;
7257         },
7258
7259         /**
7260          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7261          * @param {String} selector The CSS selector
7262          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7263          * @return {CompositeElement/CompositeElementLite} The composite element
7264          */
7265         select : function(selector, unique){
7266             return El.select(selector, unique, this.dom);
7267         },
7268
7269         /**
7270          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7271          * @param {String} selector The CSS selector
7272          * @return {Array} An array of the matched nodes
7273          */
7274         query : function(selector, unique){
7275             return Roo.DomQuery.select(selector, this.dom);
7276         },
7277
7278         /**
7279          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7280          * @param {String} selector The CSS selector
7281          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7282          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7283          */
7284         child : function(selector, returnDom){
7285             var n = Roo.DomQuery.selectNode(selector, this.dom);
7286             return returnDom ? n : Roo.get(n);
7287         },
7288
7289         /**
7290          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7291          * @param {String} selector The CSS selector
7292          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7293          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7294          */
7295         down : function(selector, returnDom){
7296             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7297             return returnDom ? n : Roo.get(n);
7298         },
7299
7300         /**
7301          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7302          * @param {String} group The group the DD object is member of
7303          * @param {Object} config The DD config object
7304          * @param {Object} overrides An object containing methods to override/implement on the DD object
7305          * @return {Roo.dd.DD} The DD object
7306          */
7307         initDD : function(group, config, overrides){
7308             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7309             return Roo.apply(dd, overrides);
7310         },
7311
7312         /**
7313          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7314          * @param {String} group The group the DDProxy object is member of
7315          * @param {Object} config The DDProxy config object
7316          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7317          * @return {Roo.dd.DDProxy} The DDProxy object
7318          */
7319         initDDProxy : function(group, config, overrides){
7320             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7321             return Roo.apply(dd, overrides);
7322         },
7323
7324         /**
7325          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7326          * @param {String} group The group the DDTarget object is member of
7327          * @param {Object} config The DDTarget config object
7328          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7329          * @return {Roo.dd.DDTarget} The DDTarget object
7330          */
7331         initDDTarget : function(group, config, overrides){
7332             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7333             return Roo.apply(dd, overrides);
7334         },
7335
7336         /**
7337          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7338          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7339          * @param {Boolean} visible Whether the element is visible
7340          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7341          * @return {Roo.Element} this
7342          */
7343          setVisible : function(visible, animate){
7344             if(!animate || !A){
7345                 if(this.visibilityMode == El.DISPLAY){
7346                     this.setDisplayed(visible);
7347                 }else{
7348                     this.fixDisplay();
7349                     this.dom.style.visibility = visible ? "visible" : "hidden";
7350                 }
7351             }else{
7352                 // closure for composites
7353                 var dom = this.dom;
7354                 var visMode = this.visibilityMode;
7355                 if(visible){
7356                     this.setOpacity(.01);
7357                     this.setVisible(true);
7358                 }
7359                 this.anim({opacity: { to: (visible?1:0) }},
7360                       this.preanim(arguments, 1),
7361                       null, .35, 'easeIn', function(){
7362                          if(!visible){
7363                              if(visMode == El.DISPLAY){
7364                                  dom.style.display = "none";
7365                              }else{
7366                                  dom.style.visibility = "hidden";
7367                              }
7368                              Roo.get(dom).setOpacity(1);
7369                          }
7370                      });
7371             }
7372             return this;
7373         },
7374
7375         /**
7376          * Returns true if display is not "none"
7377          * @return {Boolean}
7378          */
7379         isDisplayed : function() {
7380             return this.getStyle("display") != "none";
7381         },
7382
7383         /**
7384          * Toggles the element's visibility or display, depending on visibility mode.
7385          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7386          * @return {Roo.Element} this
7387          */
7388         toggle : function(animate){
7389             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7390             return this;
7391         },
7392
7393         /**
7394          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7395          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7396          * @return {Roo.Element} this
7397          */
7398         setDisplayed : function(value) {
7399             if(typeof value == "boolean"){
7400                value = value ? this.originalDisplay : "none";
7401             }
7402             this.setStyle("display", value);
7403             return this;
7404         },
7405
7406         /**
7407          * Tries to focus the element. Any exceptions are caught and ignored.
7408          * @return {Roo.Element} this
7409          */
7410         focus : function() {
7411             try{
7412                 this.dom.focus();
7413             }catch(e){}
7414             return this;
7415         },
7416
7417         /**
7418          * Tries to blur the element. Any exceptions are caught and ignored.
7419          * @return {Roo.Element} this
7420          */
7421         blur : function() {
7422             try{
7423                 this.dom.blur();
7424             }catch(e){}
7425             return this;
7426         },
7427
7428         /**
7429          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7430          * @param {String/Array} className The CSS class to add, or an array of classes
7431          * @return {Roo.Element} this
7432          */
7433         addClass : function(className){
7434             if(className instanceof Array){
7435                 for(var i = 0, len = className.length; i < len; i++) {
7436                     this.addClass(className[i]);
7437                 }
7438             }else{
7439                 if(className && !this.hasClass(className)){
7440                     this.dom.className = this.dom.className + " " + className;
7441                 }
7442             }
7443             return this;
7444         },
7445
7446         /**
7447          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7448          * @param {String/Array} className The CSS class to add, or an array of classes
7449          * @return {Roo.Element} this
7450          */
7451         radioClass : function(className){
7452             var siblings = this.dom.parentNode.childNodes;
7453             for(var i = 0; i < siblings.length; i++) {
7454                 var s = siblings[i];
7455                 if(s.nodeType == 1){
7456                     Roo.get(s).removeClass(className);
7457                 }
7458             }
7459             this.addClass(className);
7460             return this;
7461         },
7462
7463         /**
7464          * Removes one or more CSS classes from the element.
7465          * @param {String/Array} className The CSS class to remove, or an array of classes
7466          * @return {Roo.Element} this
7467          */
7468         removeClass : function(className){
7469             if(!className || !this.dom.className){
7470                 return this;
7471             }
7472             if(className instanceof Array){
7473                 for(var i = 0, len = className.length; i < len; i++) {
7474                     this.removeClass(className[i]);
7475                 }
7476             }else{
7477                 if(this.hasClass(className)){
7478                     var re = this.classReCache[className];
7479                     if (!re) {
7480                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7481                        this.classReCache[className] = re;
7482                     }
7483                     this.dom.className =
7484                         this.dom.className.replace(re, " ");
7485                 }
7486             }
7487             return this;
7488         },
7489
7490         // private
7491         classReCache: {},
7492
7493         /**
7494          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7495          * @param {String} className The CSS class to toggle
7496          * @return {Roo.Element} this
7497          */
7498         toggleClass : function(className){
7499             if(this.hasClass(className)){
7500                 this.removeClass(className);
7501             }else{
7502                 this.addClass(className);
7503             }
7504             return this;
7505         },
7506
7507         /**
7508          * Checks if the specified CSS class exists on this element's DOM node.
7509          * @param {String} className The CSS class to check for
7510          * @return {Boolean} True if the class exists, else false
7511          */
7512         hasClass : function(className){
7513             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7514         },
7515
7516         /**
7517          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7518          * @param {String} oldClassName The CSS class to replace
7519          * @param {String} newClassName The replacement CSS class
7520          * @return {Roo.Element} this
7521          */
7522         replaceClass : function(oldClassName, newClassName){
7523             this.removeClass(oldClassName);
7524             this.addClass(newClassName);
7525             return this;
7526         },
7527
7528         /**
7529          * Returns an object with properties matching the styles requested.
7530          * For example, el.getStyles('color', 'font-size', 'width') might return
7531          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7532          * @param {String} style1 A style name
7533          * @param {String} style2 A style name
7534          * @param {String} etc.
7535          * @return {Object} The style object
7536          */
7537         getStyles : function(){
7538             var a = arguments, len = a.length, r = {};
7539             for(var i = 0; i < len; i++){
7540                 r[a[i]] = this.getStyle(a[i]);
7541             }
7542             return r;
7543         },
7544
7545         /**
7546          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7547          * @param {String} property The style property whose value is returned.
7548          * @return {String} The current value of the style property for this element.
7549          */
7550         getStyle : function(){
7551             return view && view.getComputedStyle ?
7552                 function(prop){
7553                     var el = this.dom, v, cs, camel;
7554                     if(prop == 'float'){
7555                         prop = "cssFloat";
7556                     }
7557                     if(el.style && (v = el.style[prop])){
7558                         return v;
7559                     }
7560                     if(cs = view.getComputedStyle(el, "")){
7561                         if(!(camel = propCache[prop])){
7562                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7563                         }
7564                         return cs[camel];
7565                     }
7566                     return null;
7567                 } :
7568                 function(prop){
7569                     var el = this.dom, v, cs, camel;
7570                     if(prop == 'opacity'){
7571                         if(typeof el.style.filter == 'string'){
7572                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7573                             if(m){
7574                                 var fv = parseFloat(m[1]);
7575                                 if(!isNaN(fv)){
7576                                     return fv ? fv / 100 : 0;
7577                                 }
7578                             }
7579                         }
7580                         return 1;
7581                     }else if(prop == 'float'){
7582                         prop = "styleFloat";
7583                     }
7584                     if(!(camel = propCache[prop])){
7585                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7586                     }
7587                     if(v = el.style[camel]){
7588                         return v;
7589                     }
7590                     if(cs = el.currentStyle){
7591                         return cs[camel];
7592                     }
7593                     return null;
7594                 };
7595         }(),
7596
7597         /**
7598          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7599          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7600          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7601          * @return {Roo.Element} this
7602          */
7603         setStyle : function(prop, value){
7604             if(typeof prop == "string"){
7605                 
7606                 if (prop == 'float') {
7607                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7608                     return this;
7609                 }
7610                 
7611                 var camel;
7612                 if(!(camel = propCache[prop])){
7613                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7614                 }
7615                 
7616                 if(camel == 'opacity') {
7617                     this.setOpacity(value);
7618                 }else{
7619                     this.dom.style[camel] = value;
7620                 }
7621             }else{
7622                 for(var style in prop){
7623                     if(typeof prop[style] != "function"){
7624                        this.setStyle(style, prop[style]);
7625                     }
7626                 }
7627             }
7628             return this;
7629         },
7630
7631         /**
7632          * More flexible version of {@link #setStyle} for setting style properties.
7633          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7634          * a function which returns such a specification.
7635          * @return {Roo.Element} this
7636          */
7637         applyStyles : function(style){
7638             Roo.DomHelper.applyStyles(this.dom, style);
7639             return this;
7640         },
7641
7642         /**
7643           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7644           * @return {Number} The X position of the element
7645           */
7646         getX : function(){
7647             return D.getX(this.dom);
7648         },
7649
7650         /**
7651           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7652           * @return {Number} The Y position of the element
7653           */
7654         getY : function(){
7655             return D.getY(this.dom);
7656         },
7657
7658         /**
7659           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7660           * @return {Array} The XY position of the element
7661           */
7662         getXY : function(){
7663             return D.getXY(this.dom);
7664         },
7665
7666         /**
7667          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7668          * @param {Number} The X position of the element
7669          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7670          * @return {Roo.Element} this
7671          */
7672         setX : function(x, animate){
7673             if(!animate || !A){
7674                 D.setX(this.dom, x);
7675             }else{
7676                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683          * @param {Number} The Y position of the element
7684          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7685          * @return {Roo.Element} this
7686          */
7687         setY : function(y, animate){
7688             if(!animate || !A){
7689                 D.setY(this.dom, y);
7690             }else{
7691                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7698          * @param {String} left The left CSS property value
7699          * @return {Roo.Element} this
7700          */
7701         setLeft : function(left){
7702             this.setStyle("left", this.addUnits(left));
7703             return this;
7704         },
7705
7706         /**
7707          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7708          * @param {String} top The top CSS property value
7709          * @return {Roo.Element} this
7710          */
7711         setTop : function(top){
7712             this.setStyle("top", this.addUnits(top));
7713             return this;
7714         },
7715
7716         /**
7717          * Sets the element's CSS right style.
7718          * @param {String} right The right CSS property value
7719          * @return {Roo.Element} this
7720          */
7721         setRight : function(right){
7722             this.setStyle("right", this.addUnits(right));
7723             return this;
7724         },
7725
7726         /**
7727          * Sets the element's CSS bottom style.
7728          * @param {String} bottom The bottom CSS property value
7729          * @return {Roo.Element} this
7730          */
7731         setBottom : function(bottom){
7732             this.setStyle("bottom", this.addUnits(bottom));
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7738          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7739          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setXY : function(pos, animate){
7744             if(!animate || !A){
7745                 D.setXY(this.dom, pos);
7746             }else{
7747                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7754          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7755          * @param {Number} x X value for new position (coordinates are page-based)
7756          * @param {Number} y Y value for new position (coordinates are page-based)
7757          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7758          * @return {Roo.Element} this
7759          */
7760         setLocation : function(x, y, animate){
7761             this.setXY([x, y], this.preanim(arguments, 2));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7767          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7768          * @param {Number} x X value for new position (coordinates are page-based)
7769          * @param {Number} y Y value for new position (coordinates are page-based)
7770          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7771          * @return {Roo.Element} this
7772          */
7773         moveTo : function(x, y, animate){
7774             this.setXY([x, y], this.preanim(arguments, 2));
7775             return this;
7776         },
7777
7778         /**
7779          * Returns the region of the given element.
7780          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7781          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7782          */
7783         getRegion : function(){
7784             return D.getRegion(this.dom);
7785         },
7786
7787         /**
7788          * Returns the offset height of the element
7789          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7790          * @return {Number} The element's height
7791          */
7792         getHeight : function(contentHeight){
7793             var h = this.dom.offsetHeight || 0;
7794             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7795         },
7796
7797         /**
7798          * Returns the offset width of the element
7799          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7800          * @return {Number} The element's width
7801          */
7802         getWidth : function(contentWidth){
7803             var w = this.dom.offsetWidth || 0;
7804             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7805         },
7806
7807         /**
7808          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7809          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7810          * if a height has not been set using CSS.
7811          * @return {Number}
7812          */
7813         getComputedHeight : function(){
7814             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7815             if(!h){
7816                 h = parseInt(this.getStyle('height'), 10) || 0;
7817                 if(!this.isBorderBox()){
7818                     h += this.getFrameWidth('tb');
7819                 }
7820             }
7821             return h;
7822         },
7823
7824         /**
7825          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7826          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7827          * if a width has not been set using CSS.
7828          * @return {Number}
7829          */
7830         getComputedWidth : function(){
7831             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7832             if(!w){
7833                 w = parseInt(this.getStyle('width'), 10) || 0;
7834                 if(!this.isBorderBox()){
7835                     w += this.getFrameWidth('lr');
7836                 }
7837             }
7838             return w;
7839         },
7840
7841         /**
7842          * Returns the size of the element.
7843          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7844          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7845          */
7846         getSize : function(contentSize){
7847             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7848         },
7849
7850         /**
7851          * Returns the width and height of the viewport.
7852          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7853          */
7854         getViewSize : function(){
7855             var d = this.dom, doc = document, aw = 0, ah = 0;
7856             if(d == doc || d == doc.body){
7857                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7858             }else{
7859                 return {
7860                     width : d.clientWidth,
7861                     height: d.clientHeight
7862                 };
7863             }
7864         },
7865
7866         /**
7867          * Returns the value of the "value" attribute
7868          * @param {Boolean} asNumber true to parse the value as a number
7869          * @return {String/Number}
7870          */
7871         getValue : function(asNumber){
7872             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7873         },
7874
7875         // private
7876         adjustWidth : function(width){
7877             if(typeof width == "number"){
7878                 if(this.autoBoxAdjust && !this.isBorderBox()){
7879                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7880                 }
7881                 if(width < 0){
7882                     width = 0;
7883                 }
7884             }
7885             return width;
7886         },
7887
7888         // private
7889         adjustHeight : function(height){
7890             if(typeof height == "number"){
7891                if(this.autoBoxAdjust && !this.isBorderBox()){
7892                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7893                }
7894                if(height < 0){
7895                    height = 0;
7896                }
7897             }
7898             return height;
7899         },
7900
7901         /**
7902          * Set the width of the element
7903          * @param {Number} width The new width
7904          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7905          * @return {Roo.Element} this
7906          */
7907         setWidth : function(width, animate){
7908             width = this.adjustWidth(width);
7909             if(!animate || !A){
7910                 this.dom.style.width = this.addUnits(width);
7911             }else{
7912                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * Set the height of the element
7919          * @param {Number} height The new height
7920          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7921          * @return {Roo.Element} this
7922          */
7923          setHeight : function(height, animate){
7924             height = this.adjustHeight(height);
7925             if(!animate || !A){
7926                 this.dom.style.height = this.addUnits(height);
7927             }else{
7928                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7929             }
7930             return this;
7931         },
7932
7933         /**
7934          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7935          * @param {Number} width The new width
7936          * @param {Number} height The new height
7937          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940          setSize : function(width, height, animate){
7941             if(typeof width == "object"){ // in case of object from getSize()
7942                 height = width.height; width = width.width;
7943             }
7944             width = this.adjustWidth(width); height = this.adjustHeight(height);
7945             if(!animate || !A){
7946                 this.dom.style.width = this.addUnits(width);
7947                 this.dom.style.height = this.addUnits(height);
7948             }else{
7949                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7950             }
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7956          * @param {Number} x X value for new position (coordinates are page-based)
7957          * @param {Number} y Y value for new position (coordinates are page-based)
7958          * @param {Number} width The new width
7959          * @param {Number} height The new height
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setBounds : function(x, y, width, height, animate){
7964             if(!animate || !A){
7965                 this.setSize(width, height);
7966                 this.setLocation(x, y);
7967             }else{
7968                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7969                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7970                               this.preanim(arguments, 4), 'motion');
7971             }
7972             return this;
7973         },
7974
7975         /**
7976          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7977          * @param {Roo.lib.Region} region The region to fill
7978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setRegion : function(region, animate){
7982             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7983             return this;
7984         },
7985
7986         /**
7987          * Appends an event handler
7988          *
7989          * @param {String}   eventName     The type of event to append
7990          * @param {Function} fn        The method the event invokes
7991          * @param {Object} scope       (optional) The scope (this object) of the fn
7992          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7993          */
7994         addListener : function(eventName, fn, scope, options){
7995             if (this.dom) {
7996                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7997             }
7998         },
7999
8000         /**
8001          * Removes an event handler from this element
8002          * @param {String} eventName the type of event to remove
8003          * @param {Function} fn the method the event invokes
8004          * @return {Roo.Element} this
8005          */
8006         removeListener : function(eventName, fn){
8007             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8008             return this;
8009         },
8010
8011         /**
8012          * Removes all previous added listeners from this element
8013          * @return {Roo.Element} this
8014          */
8015         removeAllListeners : function(){
8016             E.purgeElement(this.dom);
8017             return this;
8018         },
8019
8020         relayEvent : function(eventName, observable){
8021             this.on(eventName, function(e){
8022                 observable.fireEvent(eventName, e);
8023             });
8024         },
8025
8026         /**
8027          * Set the opacity of the element
8028          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8029          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8030          * @return {Roo.Element} this
8031          */
8032          setOpacity : function(opacity, animate){
8033             if(!animate || !A){
8034                 var s = this.dom.style;
8035                 if(Roo.isIE){
8036                     s.zoom = 1;
8037                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8038                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8039                 }else{
8040                     s.opacity = opacity;
8041                 }
8042             }else{
8043                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8044             }
8045             return this;
8046         },
8047
8048         /**
8049          * Gets the left X coordinate
8050          * @param {Boolean} local True to get the local css position instead of page coordinate
8051          * @return {Number}
8052          */
8053         getLeft : function(local){
8054             if(!local){
8055                 return this.getX();
8056             }else{
8057                 return parseInt(this.getStyle("left"), 10) || 0;
8058             }
8059         },
8060
8061         /**
8062          * Gets the right X coordinate of the element (element X position + element width)
8063          * @param {Boolean} local True to get the local css position instead of page coordinate
8064          * @return {Number}
8065          */
8066         getRight : function(local){
8067             if(!local){
8068                 return this.getX() + this.getWidth();
8069             }else{
8070                 return (this.getLeft(true) + this.getWidth()) || 0;
8071             }
8072         },
8073
8074         /**
8075          * Gets the top Y coordinate
8076          * @param {Boolean} local True to get the local css position instead of page coordinate
8077          * @return {Number}
8078          */
8079         getTop : function(local) {
8080             if(!local){
8081                 return this.getY();
8082             }else{
8083                 return parseInt(this.getStyle("top"), 10) || 0;
8084             }
8085         },
8086
8087         /**
8088          * Gets the bottom Y coordinate of the element (element Y position + element height)
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getBottom : function(local){
8093             if(!local){
8094                 return this.getY() + this.getHeight();
8095             }else{
8096                 return (this.getTop(true) + this.getHeight()) || 0;
8097             }
8098         },
8099
8100         /**
8101         * Initializes positioning on this element. If a desired position is not passed, it will make the
8102         * the element positioned relative IF it is not already positioned.
8103         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8104         * @param {Number} zIndex (optional) The zIndex to apply
8105         * @param {Number} x (optional) Set the page X position
8106         * @param {Number} y (optional) Set the page Y position
8107         */
8108         position : function(pos, zIndex, x, y){
8109             if(!pos){
8110                if(this.getStyle('position') == 'static'){
8111                    this.setStyle('position', 'relative');
8112                }
8113             }else{
8114                 this.setStyle("position", pos);
8115             }
8116             if(zIndex){
8117                 this.setStyle("z-index", zIndex);
8118             }
8119             if(x !== undefined && y !== undefined){
8120                 this.setXY([x, y]);
8121             }else if(x !== undefined){
8122                 this.setX(x);
8123             }else if(y !== undefined){
8124                 this.setY(y);
8125             }
8126         },
8127
8128         /**
8129         * Clear positioning back to the default when the document was loaded
8130         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8131         * @return {Roo.Element} this
8132          */
8133         clearPositioning : function(value){
8134             value = value ||'';
8135             this.setStyle({
8136                 "left": value,
8137                 "right": value,
8138                 "top": value,
8139                 "bottom": value,
8140                 "z-index": "",
8141                 "position" : "static"
8142             });
8143             return this;
8144         },
8145
8146         /**
8147         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8148         * snapshot before performing an update and then restoring the element.
8149         * @return {Object}
8150         */
8151         getPositioning : function(){
8152             var l = this.getStyle("left");
8153             var t = this.getStyle("top");
8154             return {
8155                 "position" : this.getStyle("position"),
8156                 "left" : l,
8157                 "right" : l ? "" : this.getStyle("right"),
8158                 "top" : t,
8159                 "bottom" : t ? "" : this.getStyle("bottom"),
8160                 "z-index" : this.getStyle("z-index")
8161             };
8162         },
8163
8164         /**
8165          * Gets the width of the border(s) for the specified side(s)
8166          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8167          * passing lr would get the border (l)eft width + the border (r)ight width.
8168          * @return {Number} The width of the sides passed added together
8169          */
8170         getBorderWidth : function(side){
8171             return this.addStyles(side, El.borders);
8172         },
8173
8174         /**
8175          * Gets the width of the padding(s) for the specified side(s)
8176          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8177          * passing lr would get the padding (l)eft + the padding (r)ight.
8178          * @return {Number} The padding of the sides passed added together
8179          */
8180         getPadding : function(side){
8181             return this.addStyles(side, El.paddings);
8182         },
8183
8184         /**
8185         * Set positioning with an object returned by getPositioning().
8186         * @param {Object} posCfg
8187         * @return {Roo.Element} this
8188          */
8189         setPositioning : function(pc){
8190             this.applyStyles(pc);
8191             if(pc.right == "auto"){
8192                 this.dom.style.right = "";
8193             }
8194             if(pc.bottom == "auto"){
8195                 this.dom.style.bottom = "";
8196             }
8197             return this;
8198         },
8199
8200         // private
8201         fixDisplay : function(){
8202             if(this.getStyle("display") == "none"){
8203                 this.setStyle("visibility", "hidden");
8204                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8205                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8206                     this.setStyle("display", "block");
8207                 }
8208             }
8209         },
8210
8211         /**
8212          * Quick set left and top adding default units
8213          * @param {String} left The left CSS property value
8214          * @param {String} top The top CSS property value
8215          * @return {Roo.Element} this
8216          */
8217          setLeftTop : function(left, top){
8218             this.dom.style.left = this.addUnits(left);
8219             this.dom.style.top = this.addUnits(top);
8220             return this;
8221         },
8222
8223         /**
8224          * Move this element relative to its current position.
8225          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8226          * @param {Number} distance How far to move the element in pixels
8227          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8228          * @return {Roo.Element} this
8229          */
8230          move : function(direction, distance, animate){
8231             var xy = this.getXY();
8232             direction = direction.toLowerCase();
8233             switch(direction){
8234                 case "l":
8235                 case "left":
8236                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8237                     break;
8238                case "r":
8239                case "right":
8240                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "t":
8243                case "top":
8244                case "up":
8245                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8246                     break;
8247                case "b":
8248                case "bottom":
8249                case "down":
8250                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8251                     break;
8252             }
8253             return this;
8254         },
8255
8256         /**
8257          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8258          * @return {Roo.Element} this
8259          */
8260         clip : function(){
8261             if(!this.isClipped){
8262                this.isClipped = true;
8263                this.originalClip = {
8264                    "o": this.getStyle("overflow"),
8265                    "x": this.getStyle("overflow-x"),
8266                    "y": this.getStyle("overflow-y")
8267                };
8268                this.setStyle("overflow", "hidden");
8269                this.setStyle("overflow-x", "hidden");
8270                this.setStyle("overflow-y", "hidden");
8271             }
8272             return this;
8273         },
8274
8275         /**
8276          *  Return clipping (overflow) to original clipping before clip() was called
8277          * @return {Roo.Element} this
8278          */
8279         unclip : function(){
8280             if(this.isClipped){
8281                 this.isClipped = false;
8282                 var o = this.originalClip;
8283                 if(o.o){this.setStyle("overflow", o.o);}
8284                 if(o.x){this.setStyle("overflow-x", o.x);}
8285                 if(o.y){this.setStyle("overflow-y", o.y);}
8286             }
8287             return this;
8288         },
8289
8290
8291         /**
8292          * Gets the x,y coordinates specified by the anchor position on the element.
8293          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8294          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8295          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8296          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8297          * @return {Array} [x, y] An array containing the element's x and y coordinates
8298          */
8299         getAnchorXY : function(anchor, local, s){
8300             //Passing a different size is useful for pre-calculating anchors,
8301             //especially for anchored animations that change the el size.
8302
8303             var w, h, vp = false;
8304             if(!s){
8305                 var d = this.dom;
8306                 if(d == document.body || d == document){
8307                     vp = true;
8308                     w = D.getViewWidth(); h = D.getViewHeight();
8309                 }else{
8310                     w = this.getWidth(); h = this.getHeight();
8311                 }
8312             }else{
8313                 w = s.width;  h = s.height;
8314             }
8315             var x = 0, y = 0, r = Math.round;
8316             switch((anchor || "tl").toLowerCase()){
8317                 case "c":
8318                     x = r(w*.5);
8319                     y = r(h*.5);
8320                 break;
8321                 case "t":
8322                     x = r(w*.5);
8323                     y = 0;
8324                 break;
8325                 case "l":
8326                     x = 0;
8327                     y = r(h*.5);
8328                 break;
8329                 case "r":
8330                     x = w;
8331                     y = r(h*.5);
8332                 break;
8333                 case "b":
8334                     x = r(w*.5);
8335                     y = h;
8336                 break;
8337                 case "tl":
8338                     x = 0;
8339                     y = 0;
8340                 break;
8341                 case "bl":
8342                     x = 0;
8343                     y = h;
8344                 break;
8345                 case "br":
8346                     x = w;
8347                     y = h;
8348                 break;
8349                 case "tr":
8350                     x = w;
8351                     y = 0;
8352                 break;
8353             }
8354             if(local === true){
8355                 return [x, y];
8356             }
8357             if(vp){
8358                 var sc = this.getScroll();
8359                 return [x + sc.left, y + sc.top];
8360             }
8361             //Add the element's offset xy
8362             var o = this.getXY();
8363             return [x+o[0], y+o[1]];
8364         },
8365
8366         /**
8367          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8368          * supported position values.
8369          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8370          * @param {String} position The position to align to.
8371          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8372          * @return {Array} [x, y]
8373          */
8374         getAlignToXY : function(el, p, o){
8375             el = Roo.get(el);
8376             var d = this.dom;
8377             if(!el.dom){
8378                 throw "Element.alignTo with an element that doesn't exist";
8379             }
8380             var c = false; //constrain to viewport
8381             var p1 = "", p2 = "";
8382             o = o || [0,0];
8383
8384             if(!p){
8385                 p = "tl-bl";
8386             }else if(p == "?"){
8387                 p = "tl-bl?";
8388             }else if(p.indexOf("-") == -1){
8389                 p = "tl-" + p;
8390             }
8391             p = p.toLowerCase();
8392             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8393             if(!m){
8394                throw "Element.alignTo with an invalid alignment " + p;
8395             }
8396             p1 = m[1]; p2 = m[2]; c = !!m[3];
8397
8398             //Subtract the aligned el's internal xy from the target's offset xy
8399             //plus custom offset to get the aligned el's new offset xy
8400             var a1 = this.getAnchorXY(p1, true);
8401             var a2 = el.getAnchorXY(p2, false);
8402             var x = a2[0] - a1[0] + o[0];
8403             var y = a2[1] - a1[1] + o[1];
8404             if(c){
8405                 //constrain the aligned el to viewport if necessary
8406                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8407                 // 5px of margin for ie
8408                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8409
8410                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8411                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8412                 //otherwise swap the aligned el to the opposite border of the target.
8413                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8414                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8415                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8416                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8417
8418                var doc = document;
8419                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8420                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8421
8422                if((x+w) > dw + scrollX){
8423                     x = swapX ? r.left-w : dw+scrollX-w;
8424                 }
8425                if(x < scrollX){
8426                    x = swapX ? r.right : scrollX;
8427                }
8428                if((y+h) > dh + scrollY){
8429                     y = swapY ? r.top-h : dh+scrollY-h;
8430                 }
8431                if (y < scrollY){
8432                    y = swapY ? r.bottom : scrollY;
8433                }
8434             }
8435             return [x,y];
8436         },
8437
8438         // private
8439         getConstrainToXY : function(){
8440             var os = {top:0, left:0, bottom:0, right: 0};
8441
8442             return function(el, local, offsets, proposedXY){
8443                 el = Roo.get(el);
8444                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8445
8446                 var vw, vh, vx = 0, vy = 0;
8447                 if(el.dom == document.body || el.dom == document){
8448                     vw = Roo.lib.Dom.getViewWidth();
8449                     vh = Roo.lib.Dom.getViewHeight();
8450                 }else{
8451                     vw = el.dom.clientWidth;
8452                     vh = el.dom.clientHeight;
8453                     if(!local){
8454                         var vxy = el.getXY();
8455                         vx = vxy[0];
8456                         vy = vxy[1];
8457                     }
8458                 }
8459
8460                 var s = el.getScroll();
8461
8462                 vx += offsets.left + s.left;
8463                 vy += offsets.top + s.top;
8464
8465                 vw -= offsets.right;
8466                 vh -= offsets.bottom;
8467
8468                 var vr = vx+vw;
8469                 var vb = vy+vh;
8470
8471                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8472                 var x = xy[0], y = xy[1];
8473                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8474
8475                 // only move it if it needs it
8476                 var moved = false;
8477
8478                 // first validate right/bottom
8479                 if((x + w) > vr){
8480                     x = vr - w;
8481                     moved = true;
8482                 }
8483                 if((y + h) > vb){
8484                     y = vb - h;
8485                     moved = true;
8486                 }
8487                 // then make sure top/left isn't negative
8488                 if(x < vx){
8489                     x = vx;
8490                     moved = true;
8491                 }
8492                 if(y < vy){
8493                     y = vy;
8494                     moved = true;
8495                 }
8496                 return moved ? [x, y] : false;
8497             };
8498         }(),
8499
8500         // private
8501         adjustForConstraints : function(xy, parent, offsets){
8502             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8503         },
8504
8505         /**
8506          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8507          * document it aligns it to the viewport.
8508          * The position parameter is optional, and can be specified in any one of the following formats:
8509          * <ul>
8510          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8511          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8512          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8513          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8514          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8515          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8516          * </ul>
8517          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8518          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8519          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8520          * that specified in order to enforce the viewport constraints.
8521          * Following are all of the supported anchor positions:
8522     <pre>
8523     Value  Description
8524     -----  -----------------------------
8525     tl     The top left corner (default)
8526     t      The center of the top edge
8527     tr     The top right corner
8528     l      The center of the left edge
8529     c      In the center of the element
8530     r      The center of the right edge
8531     bl     The bottom left corner
8532     b      The center of the bottom edge
8533     br     The bottom right corner
8534     </pre>
8535     Example Usage:
8536     <pre><code>
8537     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8538     el.alignTo("other-el");
8539
8540     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8541     el.alignTo("other-el", "tr?");
8542
8543     // align the bottom right corner of el with the center left edge of other-el
8544     el.alignTo("other-el", "br-l?");
8545
8546     // align the center of el with the bottom left corner of other-el and
8547     // adjust the x position by -6 pixels (and the y position by 0)
8548     el.alignTo("other-el", "c-bl", [-6, 0]);
8549     </code></pre>
8550          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8551          * @param {String} position The position to align to.
8552          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8553          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8554          * @return {Roo.Element} this
8555          */
8556         alignTo : function(element, position, offsets, animate){
8557             var xy = this.getAlignToXY(element, position, offsets);
8558             this.setXY(xy, this.preanim(arguments, 3));
8559             return this;
8560         },
8561
8562         /**
8563          * Anchors an element to another element and realigns it when the window is resized.
8564          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8565          * @param {String} position The position to align to.
8566          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8567          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8568          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8569          * is a number, it is used as the buffer delay (defaults to 50ms).
8570          * @param {Function} callback The function to call after the animation finishes
8571          * @return {Roo.Element} this
8572          */
8573         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8574             var action = function(){
8575                 this.alignTo(el, alignment, offsets, animate);
8576                 Roo.callback(callback, this);
8577             };
8578             Roo.EventManager.onWindowResize(action, this);
8579             var tm = typeof monitorScroll;
8580             if(tm != 'undefined'){
8581                 Roo.EventManager.on(window, 'scroll', action, this,
8582                     {buffer: tm == 'number' ? monitorScroll : 50});
8583             }
8584             action.call(this); // align immediately
8585             return this;
8586         },
8587         /**
8588          * Clears any opacity settings from this element. Required in some cases for IE.
8589          * @return {Roo.Element} this
8590          */
8591         clearOpacity : function(){
8592             if (window.ActiveXObject) {
8593                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8594                     this.dom.style.filter = "";
8595                 }
8596             } else {
8597                 this.dom.style.opacity = "";
8598                 this.dom.style["-moz-opacity"] = "";
8599                 this.dom.style["-khtml-opacity"] = "";
8600             }
8601             return this;
8602         },
8603
8604         /**
8605          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8606          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609         hide : function(animate){
8610             this.setVisible(false, this.preanim(arguments, 0));
8611             return this;
8612         },
8613
8614         /**
8615         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8616         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8617          * @return {Roo.Element} this
8618          */
8619         show : function(animate){
8620             this.setVisible(true, this.preanim(arguments, 0));
8621             return this;
8622         },
8623
8624         /**
8625          * @private Test if size has a unit, otherwise appends the default
8626          */
8627         addUnits : function(size){
8628             return Roo.Element.addUnits(size, this.defaultUnit);
8629         },
8630
8631         /**
8632          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8633          * @return {Roo.Element} this
8634          */
8635         beginMeasure : function(){
8636             var el = this.dom;
8637             if(el.offsetWidth || el.offsetHeight){
8638                 return this; // offsets work already
8639             }
8640             var changed = [];
8641             var p = this.dom, b = document.body; // start with this element
8642             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8643                 var pe = Roo.get(p);
8644                 if(pe.getStyle('display') == 'none'){
8645                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8646                     p.style.visibility = "hidden";
8647                     p.style.display = "block";
8648                 }
8649                 p = p.parentNode;
8650             }
8651             this._measureChanged = changed;
8652             return this;
8653
8654         },
8655
8656         /**
8657          * Restores displays to before beginMeasure was called
8658          * @return {Roo.Element} this
8659          */
8660         endMeasure : function(){
8661             var changed = this._measureChanged;
8662             if(changed){
8663                 for(var i = 0, len = changed.length; i < len; i++) {
8664                     var r = changed[i];
8665                     r.el.style.visibility = r.visibility;
8666                     r.el.style.display = "none";
8667                 }
8668                 this._measureChanged = null;
8669             }
8670             return this;
8671         },
8672
8673         /**
8674         * Update the innerHTML of this element, optionally searching for and processing scripts
8675         * @param {String} html The new HTML
8676         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8677         * @param {Function} callback For async script loading you can be noticed when the update completes
8678         * @return {Roo.Element} this
8679          */
8680         update : function(html, loadScripts, callback){
8681             if(typeof html == "undefined"){
8682                 html = "";
8683             }
8684             if(loadScripts !== true){
8685                 this.dom.innerHTML = html;
8686                 if(typeof callback == "function"){
8687                     callback();
8688                 }
8689                 return this;
8690             }
8691             var id = Roo.id();
8692             var dom = this.dom;
8693
8694             html += '<span id="' + id + '"></span>';
8695
8696             E.onAvailable(id, function(){
8697                 var hd = document.getElementsByTagName("head")[0];
8698                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8699                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8700                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8701
8702                 var match;
8703                 while(match = re.exec(html)){
8704                     var attrs = match[1];
8705                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8706                     if(srcMatch && srcMatch[2]){
8707                        var s = document.createElement("script");
8708                        s.src = srcMatch[2];
8709                        var typeMatch = attrs.match(typeRe);
8710                        if(typeMatch && typeMatch[2]){
8711                            s.type = typeMatch[2];
8712                        }
8713                        hd.appendChild(s);
8714                     }else if(match[2] && match[2].length > 0){
8715                         if(window.execScript) {
8716                            window.execScript(match[2]);
8717                         } else {
8718                             /**
8719                              * eval:var:id
8720                              * eval:var:dom
8721                              * eval:var:html
8722                              * 
8723                              */
8724                            window.eval(match[2]);
8725                         }
8726                     }
8727                 }
8728                 var el = document.getElementById(id);
8729                 if(el){el.parentNode.removeChild(el);}
8730                 if(typeof callback == "function"){
8731                     callback();
8732                 }
8733             });
8734             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8735             return this;
8736         },
8737
8738         /**
8739          * Direct access to the UpdateManager update() method (takes the same parameters).
8740          * @param {String/Function} url The url for this request or a function to call to get the url
8741          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8742          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8743          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8744          * @return {Roo.Element} this
8745          */
8746         load : function(){
8747             var um = this.getUpdateManager();
8748             um.update.apply(um, arguments);
8749             return this;
8750         },
8751
8752         /**
8753         * Gets this element's UpdateManager
8754         * @return {Roo.UpdateManager} The UpdateManager
8755         */
8756         getUpdateManager : function(){
8757             if(!this.updateManager){
8758                 this.updateManager = new Roo.UpdateManager(this);
8759             }
8760             return this.updateManager;
8761         },
8762
8763         /**
8764          * Disables text selection for this element (normalized across browsers)
8765          * @return {Roo.Element} this
8766          */
8767         unselectable : function(){
8768             this.dom.unselectable = "on";
8769             this.swallowEvent("selectstart", true);
8770             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8771             this.addClass("x-unselectable");
8772             return this;
8773         },
8774
8775         /**
8776         * Calculates the x, y to center this element on the screen
8777         * @return {Array} The x, y values [x, y]
8778         */
8779         getCenterXY : function(){
8780             return this.getAlignToXY(document, 'c-c');
8781         },
8782
8783         /**
8784         * Centers the Element in either the viewport, or another Element.
8785         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8786         */
8787         center : function(centerIn){
8788             this.alignTo(centerIn || document, 'c-c');
8789             return this;
8790         },
8791
8792         /**
8793          * Tests various css rules/browsers to determine if this element uses a border box
8794          * @return {Boolean}
8795          */
8796         isBorderBox : function(){
8797             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8798         },
8799
8800         /**
8801          * Return a box {x, y, width, height} that can be used to set another elements
8802          * size/location to match this element.
8803          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8804          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8805          * @return {Object} box An object in the format {x, y, width, height}
8806          */
8807         getBox : function(contentBox, local){
8808             var xy;
8809             if(!local){
8810                 xy = this.getXY();
8811             }else{
8812                 var left = parseInt(this.getStyle("left"), 10) || 0;
8813                 var top = parseInt(this.getStyle("top"), 10) || 0;
8814                 xy = [left, top];
8815             }
8816             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8817             if(!contentBox){
8818                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8819             }else{
8820                 var l = this.getBorderWidth("l")+this.getPadding("l");
8821                 var r = this.getBorderWidth("r")+this.getPadding("r");
8822                 var t = this.getBorderWidth("t")+this.getPadding("t");
8823                 var b = this.getBorderWidth("b")+this.getPadding("b");
8824                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8825             }
8826             bx.right = bx.x + bx.width;
8827             bx.bottom = bx.y + bx.height;
8828             return bx;
8829         },
8830
8831         /**
8832          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8833          for more information about the sides.
8834          * @param {String} sides
8835          * @return {Number}
8836          */
8837         getFrameWidth : function(sides, onlyContentBox){
8838             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8839         },
8840
8841         /**
8842          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8843          * @param {Object} box The box to fill {x, y, width, height}
8844          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8846          * @return {Roo.Element} this
8847          */
8848         setBox : function(box, adjust, animate){
8849             var w = box.width, h = box.height;
8850             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8851                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8852                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8853             }
8854             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8855             return this;
8856         },
8857
8858         /**
8859          * Forces the browser to repaint this element
8860          * @return {Roo.Element} this
8861          */
8862          repaint : function(){
8863             var dom = this.dom;
8864             this.addClass("x-repaint");
8865             setTimeout(function(){
8866                 Roo.get(dom).removeClass("x-repaint");
8867             }, 1);
8868             return this;
8869         },
8870
8871         /**
8872          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8873          * then it returns the calculated width of the sides (see getPadding)
8874          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8875          * @return {Object/Number}
8876          */
8877         getMargins : function(side){
8878             if(!side){
8879                 return {
8880                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8881                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8882                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8883                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8884                 };
8885             }else{
8886                 return this.addStyles(side, El.margins);
8887              }
8888         },
8889
8890         // private
8891         addStyles : function(sides, styles){
8892             var val = 0, v, w;
8893             for(var i = 0, len = sides.length; i < len; i++){
8894                 v = this.getStyle(styles[sides.charAt(i)]);
8895                 if(v){
8896                      w = parseInt(v, 10);
8897                      if(w){ val += w; }
8898                 }
8899             }
8900             return val;
8901         },
8902
8903         /**
8904          * Creates a proxy element of this element
8905          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8906          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8907          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8908          * @return {Roo.Element} The new proxy element
8909          */
8910         createProxy : function(config, renderTo, matchBox){
8911             if(renderTo){
8912                 renderTo = Roo.getDom(renderTo);
8913             }else{
8914                 renderTo = document.body;
8915             }
8916             config = typeof config == "object" ?
8917                 config : {tag : "div", cls: config};
8918             var proxy = Roo.DomHelper.append(renderTo, config, true);
8919             if(matchBox){
8920                proxy.setBox(this.getBox());
8921             }
8922             return proxy;
8923         },
8924
8925         /**
8926          * Puts a mask over this element to disable user interaction. Requires core.css.
8927          * This method can only be applied to elements which accept child nodes.
8928          * @param {String} msg (optional) A message to display in the mask
8929          * @param {String} msgCls (optional) A css class to apply to the msg element
8930          * @return {Element} The mask  element
8931          */
8932         mask : function(msg, msgCls)
8933         {
8934             if(this.getStyle("position") == "static"){
8935                 this.setStyle("position", "relative");
8936             }
8937             if(!this._mask){
8938                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8939             }
8940             this.addClass("x-masked");
8941             this._mask.setDisplayed(true);
8942             
8943             // we wander
8944             var z = 0;
8945             var dom = this.dom
8946             while (dom && dom.style) {
8947                 if (!isNaN(parseInt(dom.style.zIndex))) {
8948                     z = Math.max(z, parseInt(dom.style.zIndex));
8949                 }
8950                 dom = dom.parentNode;
8951             }
8952             // if we are masking the body - then it hides everything..
8953             if (this.dom == document.body) {
8954                 z = 1000000;
8955                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8956                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8957             }
8958            
8959             if(typeof msg == 'string'){
8960                 if(!this._maskMsg){
8961                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8962                 }
8963                 var mm = this._maskMsg;
8964                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8965                 mm.dom.firstChild.innerHTML = msg;
8966                 mm.setDisplayed(true);
8967                 mm.center(this);
8968                 mm.setStyle('z-index', z + 102);
8969             }
8970             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8971                 this._mask.setHeight(this.getHeight());
8972             }
8973             this._mask.setStyle('z-index', z + 100);
8974             
8975             return this._mask;
8976         },
8977
8978         /**
8979          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8980          * it is cached for reuse.
8981          */
8982         unmask : function(removeEl){
8983             if(this._mask){
8984                 if(removeEl === true){
8985                     this._mask.remove();
8986                     delete this._mask;
8987                     if(this._maskMsg){
8988                         this._maskMsg.remove();
8989                         delete this._maskMsg;
8990                     }
8991                 }else{
8992                     this._mask.setDisplayed(false);
8993                     if(this._maskMsg){
8994                         this._maskMsg.setDisplayed(false);
8995                     }
8996                 }
8997             }
8998             this.removeClass("x-masked");
8999         },
9000
9001         /**
9002          * Returns true if this element is masked
9003          * @return {Boolean}
9004          */
9005         isMasked : function(){
9006             return this._mask && this._mask.isVisible();
9007         },
9008
9009         /**
9010          * Creates an iframe shim for this element to keep selects and other windowed objects from
9011          * showing through.
9012          * @return {Roo.Element} The new shim element
9013          */
9014         createShim : function(){
9015             var el = document.createElement('iframe');
9016             el.frameBorder = 'no';
9017             el.className = 'roo-shim';
9018             if(Roo.isIE && Roo.isSecure){
9019                 el.src = Roo.SSL_SECURE_URL;
9020             }
9021             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9022             shim.autoBoxAdjust = false;
9023             return shim;
9024         },
9025
9026         /**
9027          * Removes this element from the DOM and deletes it from the cache
9028          */
9029         remove : function(){
9030             if(this.dom.parentNode){
9031                 this.dom.parentNode.removeChild(this.dom);
9032             }
9033             delete El.cache[this.dom.id];
9034         },
9035
9036         /**
9037          * Sets up event handlers to add and remove a css class when the mouse is over this element
9038          * @param {String} className
9039          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9040          * mouseout events for children elements
9041          * @return {Roo.Element} this
9042          */
9043         addClassOnOver : function(className, preventFlicker){
9044             this.on("mouseover", function(){
9045                 Roo.fly(this, '_internal').addClass(className);
9046             }, this.dom);
9047             var removeFn = function(e){
9048                 if(preventFlicker !== true || !e.within(this, true)){
9049                     Roo.fly(this, '_internal').removeClass(className);
9050                 }
9051             };
9052             this.on("mouseout", removeFn, this.dom);
9053             return this;
9054         },
9055
9056         /**
9057          * Sets up event handlers to add and remove a css class when this element has the focus
9058          * @param {String} className
9059          * @return {Roo.Element} this
9060          */
9061         addClassOnFocus : function(className){
9062             this.on("focus", function(){
9063                 Roo.fly(this, '_internal').addClass(className);
9064             }, this.dom);
9065             this.on("blur", function(){
9066                 Roo.fly(this, '_internal').removeClass(className);
9067             }, this.dom);
9068             return this;
9069         },
9070         /**
9071          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9072          * @param {String} className
9073          * @return {Roo.Element} this
9074          */
9075         addClassOnClick : function(className){
9076             var dom = this.dom;
9077             this.on("mousedown", function(){
9078                 Roo.fly(dom, '_internal').addClass(className);
9079                 var d = Roo.get(document);
9080                 var fn = function(){
9081                     Roo.fly(dom, '_internal').removeClass(className);
9082                     d.removeListener("mouseup", fn);
9083                 };
9084                 d.on("mouseup", fn);
9085             });
9086             return this;
9087         },
9088
9089         /**
9090          * Stops the specified event from bubbling and optionally prevents the default action
9091          * @param {String} eventName
9092          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9093          * @return {Roo.Element} this
9094          */
9095         swallowEvent : function(eventName, preventDefault){
9096             var fn = function(e){
9097                 e.stopPropagation();
9098                 if(preventDefault){
9099                     e.preventDefault();
9100                 }
9101             };
9102             if(eventName instanceof Array){
9103                 for(var i = 0, len = eventName.length; i < len; i++){
9104                      this.on(eventName[i], fn);
9105                 }
9106                 return this;
9107             }
9108             this.on(eventName, fn);
9109             return this;
9110         },
9111
9112         /**
9113          * @private
9114          */
9115       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9116
9117         /**
9118          * Sizes this element to its parent element's dimensions performing
9119          * neccessary box adjustments.
9120          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9121          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9122          * @return {Roo.Element} this
9123          */
9124         fitToParent : function(monitorResize, targetParent) {
9125           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9126           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9127           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9128             return;
9129           }
9130           var p = Roo.get(targetParent || this.dom.parentNode);
9131           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9132           if (monitorResize === true) {
9133             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9134             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9135           }
9136           return this;
9137         },
9138
9139         /**
9140          * Gets the next sibling, skipping text nodes
9141          * @return {HTMLElement} The next sibling or null
9142          */
9143         getNextSibling : function(){
9144             var n = this.dom.nextSibling;
9145             while(n && n.nodeType != 1){
9146                 n = n.nextSibling;
9147             }
9148             return n;
9149         },
9150
9151         /**
9152          * Gets the previous sibling, skipping text nodes
9153          * @return {HTMLElement} The previous sibling or null
9154          */
9155         getPrevSibling : function(){
9156             var n = this.dom.previousSibling;
9157             while(n && n.nodeType != 1){
9158                 n = n.previousSibling;
9159             }
9160             return n;
9161         },
9162
9163
9164         /**
9165          * Appends the passed element(s) to this element
9166          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9167          * @return {Roo.Element} this
9168          */
9169         appendChild: function(el){
9170             el = Roo.get(el);
9171             el.appendTo(this);
9172             return this;
9173         },
9174
9175         /**
9176          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9177          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9178          * automatically generated with the specified attributes.
9179          * @param {HTMLElement} insertBefore (optional) a child element of this element
9180          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9181          * @return {Roo.Element} The new child element
9182          */
9183         createChild: function(config, insertBefore, returnDom){
9184             config = config || {tag:'div'};
9185             if(insertBefore){
9186                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9187             }
9188             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9189         },
9190
9191         /**
9192          * Appends this element to the passed element
9193          * @param {String/HTMLElement/Element} el The new parent element
9194          * @return {Roo.Element} this
9195          */
9196         appendTo: function(el){
9197             el = Roo.getDom(el);
9198             el.appendChild(this.dom);
9199             return this;
9200         },
9201
9202         /**
9203          * Inserts this element before the passed element in the DOM
9204          * @param {String/HTMLElement/Element} el The element to insert before
9205          * @return {Roo.Element} this
9206          */
9207         insertBefore: function(el){
9208             el = Roo.getDom(el);
9209             el.parentNode.insertBefore(this.dom, el);
9210             return this;
9211         },
9212
9213         /**
9214          * Inserts this element after the passed element in the DOM
9215          * @param {String/HTMLElement/Element} el The element to insert after
9216          * @return {Roo.Element} this
9217          */
9218         insertAfter: function(el){
9219             el = Roo.getDom(el);
9220             el.parentNode.insertBefore(this.dom, el.nextSibling);
9221             return this;
9222         },
9223
9224         /**
9225          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9226          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9227          * @return {Roo.Element} The new child
9228          */
9229         insertFirst: function(el, returnDom){
9230             el = el || {};
9231             if(typeof el == 'object' && !el.nodeType){ // dh config
9232                 return this.createChild(el, this.dom.firstChild, returnDom);
9233             }else{
9234                 el = Roo.getDom(el);
9235                 this.dom.insertBefore(el, this.dom.firstChild);
9236                 return !returnDom ? Roo.get(el) : el;
9237             }
9238         },
9239
9240         /**
9241          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9242          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9243          * @param {String} where (optional) 'before' or 'after' defaults to before
9244          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9245          * @return {Roo.Element} the inserted Element
9246          */
9247         insertSibling: function(el, where, returnDom){
9248             where = where ? where.toLowerCase() : 'before';
9249             el = el || {};
9250             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9251
9252             if(typeof el == 'object' && !el.nodeType){ // dh config
9253                 if(where == 'after' && !this.dom.nextSibling){
9254                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9255                 }else{
9256                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9257                 }
9258
9259             }else{
9260                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9261                             where == 'before' ? this.dom : this.dom.nextSibling);
9262                 if(!returnDom){
9263                     rt = Roo.get(rt);
9264                 }
9265             }
9266             return rt;
9267         },
9268
9269         /**
9270          * Creates and wraps this element with another element
9271          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9272          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9273          * @return {HTMLElement/Element} The newly created wrapper element
9274          */
9275         wrap: function(config, returnDom){
9276             if(!config){
9277                 config = {tag: "div"};
9278             }
9279             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9280             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9281             return newEl;
9282         },
9283
9284         /**
9285          * Replaces the passed element with this element
9286          * @param {String/HTMLElement/Element} el The element to replace
9287          * @return {Roo.Element} this
9288          */
9289         replace: function(el){
9290             el = Roo.get(el);
9291             this.insertBefore(el);
9292             el.remove();
9293             return this;
9294         },
9295
9296         /**
9297          * Inserts an html fragment into this element
9298          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9299          * @param {String} html The HTML fragment
9300          * @param {Boolean} returnEl True to return an Roo.Element
9301          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9302          */
9303         insertHtml : function(where, html, returnEl){
9304             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9305             return returnEl ? Roo.get(el) : el;
9306         },
9307
9308         /**
9309          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9310          * @param {Object} o The object with the attributes
9311          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9312          * @return {Roo.Element} this
9313          */
9314         set : function(o, useSet){
9315             var el = this.dom;
9316             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9317             for(var attr in o){
9318                 if(attr == "style" || typeof o[attr] == "function") continue;
9319                 if(attr=="cls"){
9320                     el.className = o["cls"];
9321                 }else{
9322                     if(useSet) el.setAttribute(attr, o[attr]);
9323                     else el[attr] = o[attr];
9324                 }
9325             }
9326             if(o.style){
9327                 Roo.DomHelper.applyStyles(el, o.style);
9328             }
9329             return this;
9330         },
9331
9332         /**
9333          * Convenience method for constructing a KeyMap
9334          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9335          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9336          * @param {Function} fn The function to call
9337          * @param {Object} scope (optional) The scope of the function
9338          * @return {Roo.KeyMap} The KeyMap created
9339          */
9340         addKeyListener : function(key, fn, scope){
9341             var config;
9342             if(typeof key != "object" || key instanceof Array){
9343                 config = {
9344                     key: key,
9345                     fn: fn,
9346                     scope: scope
9347                 };
9348             }else{
9349                 config = {
9350                     key : key.key,
9351                     shift : key.shift,
9352                     ctrl : key.ctrl,
9353                     alt : key.alt,
9354                     fn: fn,
9355                     scope: scope
9356                 };
9357             }
9358             return new Roo.KeyMap(this, config);
9359         },
9360
9361         /**
9362          * Creates a KeyMap for this element
9363          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9364          * @return {Roo.KeyMap} The KeyMap created
9365          */
9366         addKeyMap : function(config){
9367             return new Roo.KeyMap(this, config);
9368         },
9369
9370         /**
9371          * Returns true if this element is scrollable.
9372          * @return {Boolean}
9373          */
9374          isScrollable : function(){
9375             var dom = this.dom;
9376             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9377         },
9378
9379         /**
9380          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9381          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9382          * @param {Number} value The new scroll value
9383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9384          * @return {Element} this
9385          */
9386
9387         scrollTo : function(side, value, animate){
9388             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9389             if(!animate || !A){
9390                 this.dom[prop] = value;
9391             }else{
9392                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9393                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9394             }
9395             return this;
9396         },
9397
9398         /**
9399          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9400          * within this element's scrollable range.
9401          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9402          * @param {Number} distance How far to scroll the element in pixels
9403          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9404          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9405          * was scrolled as far as it could go.
9406          */
9407          scroll : function(direction, distance, animate){
9408              if(!this.isScrollable()){
9409                  return;
9410              }
9411              var el = this.dom;
9412              var l = el.scrollLeft, t = el.scrollTop;
9413              var w = el.scrollWidth, h = el.scrollHeight;
9414              var cw = el.clientWidth, ch = el.clientHeight;
9415              direction = direction.toLowerCase();
9416              var scrolled = false;
9417              var a = this.preanim(arguments, 2);
9418              switch(direction){
9419                  case "l":
9420                  case "left":
9421                      if(w - l > cw){
9422                          var v = Math.min(l + distance, w-cw);
9423                          this.scrollTo("left", v, a);
9424                          scrolled = true;
9425                      }
9426                      break;
9427                 case "r":
9428                 case "right":
9429                      if(l > 0){
9430                          var v = Math.max(l - distance, 0);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "t":
9436                 case "top":
9437                 case "up":
9438                      if(t > 0){
9439                          var v = Math.max(t - distance, 0);
9440                          this.scrollTo("top", v, a);
9441                          scrolled = true;
9442                      }
9443                      break;
9444                 case "b":
9445                 case "bottom":
9446                 case "down":
9447                      if(h - t > ch){
9448                          var v = Math.min(t + distance, h-ch);
9449                          this.scrollTo("top", v, a);
9450                          scrolled = true;
9451                      }
9452                      break;
9453              }
9454              return scrolled;
9455         },
9456
9457         /**
9458          * Translates the passed page coordinates into left/top css values for this element
9459          * @param {Number/Array} x The page x or an array containing [x, y]
9460          * @param {Number} y The page y
9461          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9462          */
9463         translatePoints : function(x, y){
9464             if(typeof x == 'object' || x instanceof Array){
9465                 y = x[1]; x = x[0];
9466             }
9467             var p = this.getStyle('position');
9468             var o = this.getXY();
9469
9470             var l = parseInt(this.getStyle('left'), 10);
9471             var t = parseInt(this.getStyle('top'), 10);
9472
9473             if(isNaN(l)){
9474                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9475             }
9476             if(isNaN(t)){
9477                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9478             }
9479
9480             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9481         },
9482
9483         /**
9484          * Returns the current scroll position of the element.
9485          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9486          */
9487         getScroll : function(){
9488             var d = this.dom, doc = document;
9489             if(d == doc || d == doc.body){
9490                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9491                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9492                 return {left: l, top: t};
9493             }else{
9494                 return {left: d.scrollLeft, top: d.scrollTop};
9495             }
9496         },
9497
9498         /**
9499          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9500          * are convert to standard 6 digit hex color.
9501          * @param {String} attr The css attribute
9502          * @param {String} defaultValue The default value to use when a valid color isn't found
9503          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9504          * YUI color anims.
9505          */
9506         getColor : function(attr, defaultValue, prefix){
9507             var v = this.getStyle(attr);
9508             if(!v || v == "transparent" || v == "inherit") {
9509                 return defaultValue;
9510             }
9511             var color = typeof prefix == "undefined" ? "#" : prefix;
9512             if(v.substr(0, 4) == "rgb("){
9513                 var rvs = v.slice(4, v.length -1).split(",");
9514                 for(var i = 0; i < 3; i++){
9515                     var h = parseInt(rvs[i]).toString(16);
9516                     if(h < 16){
9517                         h = "0" + h;
9518                     }
9519                     color += h;
9520                 }
9521             } else {
9522                 if(v.substr(0, 1) == "#"){
9523                     if(v.length == 4) {
9524                         for(var i = 1; i < 4; i++){
9525                             var c = v.charAt(i);
9526                             color +=  c + c;
9527                         }
9528                     }else if(v.length == 7){
9529                         color += v.substr(1);
9530                     }
9531                 }
9532             }
9533             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9534         },
9535
9536         /**
9537          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9538          * gradient background, rounded corners and a 4-way shadow.
9539          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9540          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9541          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9542          * @return {Roo.Element} this
9543          */
9544         boxWrap : function(cls){
9545             cls = cls || 'x-box';
9546             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9547             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9548             return el;
9549         },
9550
9551         /**
9552          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9553          * @param {String} namespace The namespace in which to look for the attribute
9554          * @param {String} name The attribute name
9555          * @return {String} The attribute value
9556          */
9557         getAttributeNS : Roo.isIE ? function(ns, name){
9558             var d = this.dom;
9559             var type = typeof d[ns+":"+name];
9560             if(type != 'undefined' && type != 'unknown'){
9561                 return d[ns+":"+name];
9562             }
9563             return d[name];
9564         } : function(ns, name){
9565             var d = this.dom;
9566             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9567         },
9568         
9569         
9570         /**
9571          * Sets or Returns the value the dom attribute value
9572          * @param {String} name The attribute name
9573          * @param {String} value (optional) The value to set the attribute to
9574          * @return {String} The attribute value
9575          */
9576         attr : function(name){
9577             if (arguments.length > 1) {
9578                 this.dom.setAttribute(name, arguments[1]);
9579                 return arguments[1];
9580             }
9581             if (!this.dom.hasAttribute(name)) {
9582                 return undefined;
9583             }
9584             return this.dom.getAttribute(name);
9585         }
9586         
9587         
9588         
9589     };
9590
9591     var ep = El.prototype;
9592
9593     /**
9594      * Appends an event handler (Shorthand for addListener)
9595      * @param {String}   eventName     The type of event to append
9596      * @param {Function} fn        The method the event invokes
9597      * @param {Object} scope       (optional) The scope (this object) of the fn
9598      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9599      * @method
9600      */
9601     ep.on = ep.addListener;
9602         // backwards compat
9603     ep.mon = ep.addListener;
9604
9605     /**
9606      * Removes an event handler from this element (shorthand for removeListener)
9607      * @param {String} eventName the type of event to remove
9608      * @param {Function} fn the method the event invokes
9609      * @return {Roo.Element} this
9610      * @method
9611      */
9612     ep.un = ep.removeListener;
9613
9614     /**
9615      * true to automatically adjust width and height settings for box-model issues (default to true)
9616      */
9617     ep.autoBoxAdjust = true;
9618
9619     // private
9620     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9621
9622     // private
9623     El.addUnits = function(v, defaultUnit){
9624         if(v === "" || v == "auto"){
9625             return v;
9626         }
9627         if(v === undefined){
9628             return '';
9629         }
9630         if(typeof v == "number" || !El.unitPattern.test(v)){
9631             return v + (defaultUnit || 'px');
9632         }
9633         return v;
9634     };
9635
9636     // special markup used throughout Roo when box wrapping elements
9637     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9638     /**
9639      * Visibility mode constant - Use visibility to hide element
9640      * @static
9641      * @type Number
9642      */
9643     El.VISIBILITY = 1;
9644     /**
9645      * Visibility mode constant - Use display to hide element
9646      * @static
9647      * @type Number
9648      */
9649     El.DISPLAY = 2;
9650
9651     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9652     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9653     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9654
9655
9656
9657     /**
9658      * @private
9659      */
9660     El.cache = {};
9661
9662     var docEl;
9663
9664     /**
9665      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9666      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9667      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9668      * @return {Element} The Element object
9669      * @static
9670      */
9671     El.get = function(el){
9672         var ex, elm, id;
9673         if(!el){ return null; }
9674         if(typeof el == "string"){ // element id
9675             if(!(elm = document.getElementById(el))){
9676                 return null;
9677             }
9678             if(ex = El.cache[el]){
9679                 ex.dom = elm;
9680             }else{
9681                 ex = El.cache[el] = new El(elm);
9682             }
9683             return ex;
9684         }else if(el.tagName){ // dom element
9685             if(!(id = el.id)){
9686                 id = Roo.id(el);
9687             }
9688             if(ex = El.cache[id]){
9689                 ex.dom = el;
9690             }else{
9691                 ex = El.cache[id] = new El(el);
9692             }
9693             return ex;
9694         }else if(el instanceof El){
9695             if(el != docEl){
9696                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9697                                                               // catch case where it hasn't been appended
9698                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9699             }
9700             return el;
9701         }else if(el.isComposite){
9702             return el;
9703         }else if(el instanceof Array){
9704             return El.select(el);
9705         }else if(el == document){
9706             // create a bogus element object representing the document object
9707             if(!docEl){
9708                 var f = function(){};
9709                 f.prototype = El.prototype;
9710                 docEl = new f();
9711                 docEl.dom = document;
9712             }
9713             return docEl;
9714         }
9715         return null;
9716     };
9717
9718     // private
9719     El.uncache = function(el){
9720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9721             if(a[i]){
9722                 delete El.cache[a[i].id || a[i]];
9723             }
9724         }
9725     };
9726
9727     // private
9728     // Garbage collection - uncache elements/purge listeners on orphaned elements
9729     // so we don't hold a reference and cause the browser to retain them
9730     El.garbageCollect = function(){
9731         if(!Roo.enableGarbageCollector){
9732             clearInterval(El.collectorThread);
9733             return;
9734         }
9735         for(var eid in El.cache){
9736             var el = El.cache[eid], d = el.dom;
9737             // -------------------------------------------------------
9738             // Determining what is garbage:
9739             // -------------------------------------------------------
9740             // !d
9741             // dom node is null, definitely garbage
9742             // -------------------------------------------------------
9743             // !d.parentNode
9744             // no parentNode == direct orphan, definitely garbage
9745             // -------------------------------------------------------
9746             // !d.offsetParent && !document.getElementById(eid)
9747             // display none elements have no offsetParent so we will
9748             // also try to look it up by it's id. However, check
9749             // offsetParent first so we don't do unneeded lookups.
9750             // This enables collection of elements that are not orphans
9751             // directly, but somewhere up the line they have an orphan
9752             // parent.
9753             // -------------------------------------------------------
9754             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9755                 delete El.cache[eid];
9756                 if(d && Roo.enableListenerCollection){
9757                     E.purgeElement(d);
9758                 }
9759             }
9760         }
9761     }
9762     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9763
9764
9765     // dom is optional
9766     El.Flyweight = function(dom){
9767         this.dom = dom;
9768     };
9769     El.Flyweight.prototype = El.prototype;
9770
9771     El._flyweights = {};
9772     /**
9773      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9774      * the dom node can be overwritten by other code.
9775      * @param {String/HTMLElement} el The dom node or id
9776      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9777      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9778      * @static
9779      * @return {Element} The shared Element object
9780      */
9781     El.fly = function(el, named){
9782         named = named || '_global';
9783         el = Roo.getDom(el);
9784         if(!el){
9785             return null;
9786         }
9787         if(!El._flyweights[named]){
9788             El._flyweights[named] = new El.Flyweight();
9789         }
9790         El._flyweights[named].dom = el;
9791         return El._flyweights[named];
9792     };
9793
9794     /**
9795      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9796      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9797      * Shorthand of {@link Roo.Element#get}
9798      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9799      * @return {Element} The Element object
9800      * @member Roo
9801      * @method get
9802      */
9803     Roo.get = El.get;
9804     /**
9805      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9806      * the dom node can be overwritten by other code.
9807      * Shorthand of {@link Roo.Element#fly}
9808      * @param {String/HTMLElement} el The dom node or id
9809      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9810      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9811      * @static
9812      * @return {Element} The shared Element object
9813      * @member Roo
9814      * @method fly
9815      */
9816     Roo.fly = El.fly;
9817
9818     // speedy lookup for elements never to box adjust
9819     var noBoxAdjust = Roo.isStrict ? {
9820         select:1
9821     } : {
9822         input:1, select:1, textarea:1
9823     };
9824     if(Roo.isIE || Roo.isGecko){
9825         noBoxAdjust['button'] = 1;
9826     }
9827
9828
9829     Roo.EventManager.on(window, 'unload', function(){
9830         delete El.cache;
9831         delete El._flyweights;
9832     });
9833 })();
9834
9835
9836
9837
9838 if(Roo.DomQuery){
9839     Roo.Element.selectorFunction = Roo.DomQuery.select;
9840 }
9841
9842 Roo.Element.select = function(selector, unique, root){
9843     var els;
9844     if(typeof selector == "string"){
9845         els = Roo.Element.selectorFunction(selector, root);
9846     }else if(selector.length !== undefined){
9847         els = selector;
9848     }else{
9849         throw "Invalid selector";
9850     }
9851     if(unique === true){
9852         return new Roo.CompositeElement(els);
9853     }else{
9854         return new Roo.CompositeElementLite(els);
9855     }
9856 };
9857 /**
9858  * Selects elements based on the passed CSS selector to enable working on them as 1.
9859  * @param {String/Array} selector The CSS selector or an array of elements
9860  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9861  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9862  * @return {CompositeElementLite/CompositeElement}
9863  * @member Roo
9864  * @method select
9865  */
9866 Roo.select = Roo.Element.select;
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881 /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893
9894 //Notifies Element that fx methods are available
9895 Roo.enableFx = true;
9896
9897 /**
9898  * @class Roo.Fx
9899  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9900  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9901  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9902  * Element effects to work.</p><br/>
9903  *
9904  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9905  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9906  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9907  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9908  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9909  * expected results and should be done with care.</p><br/>
9910  *
9911  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9912  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9913 <pre>
9914 Value  Description
9915 -----  -----------------------------
9916 tl     The top left corner
9917 t      The center of the top edge
9918 tr     The top right corner
9919 l      The center of the left edge
9920 r      The center of the right edge
9921 bl     The bottom left corner
9922 b      The center of the bottom edge
9923 br     The bottom right corner
9924 </pre>
9925  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9926  * below are common options that can be passed to any Fx method.</b>
9927  * @cfg {Function} callback A function called when the effect is finished
9928  * @cfg {Object} scope The scope of the effect function
9929  * @cfg {String} easing A valid Easing value for the effect
9930  * @cfg {String} afterCls A css class to apply after the effect
9931  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9932  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9933  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9934  * effects that end with the element being visually hidden, ignored otherwise)
9935  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9936  * a function which returns such a specification that will be applied to the Element after the effect finishes
9937  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9938  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9939  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9940  */
9941 Roo.Fx = {
9942         /**
9943          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9944          * origin for the slide effect.  This function automatically handles wrapping the element with
9945          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element in from the top
9949 el.slideIn();
9950
9951 // custom: slide the element in from the right with a 2-second duration
9952 el.slideIn('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideIn('t', {
9956     easing: 'easeOut',
9957     duration: .5
9958 });
9959 </code></pre>
9960          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9961          * @param {Object} options (optional) Object literal with any of the Fx config options
9962          * @return {Roo.Element} The Element
9963          */
9964     slideIn : function(anchor, o){
9965         var el = this.getFxEl();
9966         o = o || {};
9967
9968         el.queueFx(o, function(){
9969
9970             anchor = anchor || "t";
9971
9972             // fix display to visibility
9973             this.fixDisplay();
9974
9975             // restore values after effect
9976             var r = this.getFxRestore();
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "hidden");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             // clear out temp styles after slide and unwrap
9989             var after = function(){
9990                 el.fxUnwrap(wrap, r.pos, o);
9991                 st.width = r.width;
9992                 st.height = r.height;
9993                 el.afterFx(o);
9994             };
9995             // time to calc the positions
9996             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9997
9998             switch(anchor.toLowerCase()){
9999                 case "t":
10000                     wrap.setSize(b.width, 0);
10001                     st.left = st.bottom = "0";
10002                     a = {height: bh};
10003                 break;
10004                 case "l":
10005                     wrap.setSize(0, b.height);
10006                     st.right = st.top = "0";
10007                     a = {width: bw};
10008                 break;
10009                 case "r":
10010                     wrap.setSize(0, b.height);
10011                     wrap.setX(b.right);
10012                     st.left = st.top = "0";
10013                     a = {width: bw, points: pt};
10014                 break;
10015                 case "b":
10016                     wrap.setSize(b.width, 0);
10017                     wrap.setY(b.bottom);
10018                     st.left = st.top = "0";
10019                     a = {height: bh, points: pt};
10020                 break;
10021                 case "tl":
10022                     wrap.setSize(0, 0);
10023                     st.right = st.bottom = "0";
10024                     a = {width: bw, height: bh};
10025                 break;
10026                 case "bl":
10027                     wrap.setSize(0, 0);
10028                     wrap.setY(b.y+b.height);
10029                     st.right = st.top = "0";
10030                     a = {width: bw, height: bh, points: pt};
10031                 break;
10032                 case "br":
10033                     wrap.setSize(0, 0);
10034                     wrap.setXY([b.right, b.bottom]);
10035                     st.left = st.top = "0";
10036                     a = {width: bw, height: bh, points: pt};
10037                 break;
10038                 case "tr":
10039                     wrap.setSize(0, 0);
10040                     wrap.setX(b.x+b.width);
10041                     st.left = st.bottom = "0";
10042                     a = {width: bw, height: bh, points: pt};
10043                 break;
10044             }
10045             this.dom.style.visibility = "visible";
10046             wrap.show();
10047
10048             arguments.callee.anim = wrap.fxanim(a,
10049                 o,
10050                 'motion',
10051                 .5,
10052                 'easeOut', after);
10053         });
10054         return this;
10055     },
10056     
10057         /**
10058          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10059          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10060          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10061          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10062          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10063          * Usage:
10064          *<pre><code>
10065 // default: slide the element out to the top
10066 el.slideOut();
10067
10068 // custom: slide the element out to the right with a 2-second duration
10069 el.slideOut('r', { duration: 2 });
10070
10071 // common config options shown with default values
10072 el.slideOut('t', {
10073     easing: 'easeOut',
10074     duration: .5,
10075     remove: false,
10076     useDisplay: false
10077 });
10078 </code></pre>
10079          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10080          * @param {Object} options (optional) Object literal with any of the Fx config options
10081          * @return {Roo.Element} The Element
10082          */
10083     slideOut : function(anchor, o){
10084         var el = this.getFxEl();
10085         o = o || {};
10086
10087         el.queueFx(o, function(){
10088
10089             anchor = anchor || "t";
10090
10091             // restore values after effect
10092             var r = this.getFxRestore();
10093             
10094             var b = this.getBox();
10095             // fixed size for slide
10096             this.setSize(b);
10097
10098             // wrap if needed
10099             var wrap = this.fxWrap(r.pos, o, "visible");
10100
10101             var st = this.dom.style;
10102             st.visibility = "visible";
10103             st.position = "absolute";
10104
10105             wrap.setSize(b);
10106
10107             var after = function(){
10108                 if(o.useDisplay){
10109                     el.setDisplayed(false);
10110                 }else{
10111                     el.hide();
10112                 }
10113
10114                 el.fxUnwrap(wrap, r.pos, o);
10115
10116                 st.width = r.width;
10117                 st.height = r.height;
10118
10119                 el.afterFx(o);
10120             };
10121
10122             var a, zero = {to: 0};
10123             switch(anchor.toLowerCase()){
10124                 case "t":
10125                     st.left = st.bottom = "0";
10126                     a = {height: zero};
10127                 break;
10128                 case "l":
10129                     st.right = st.top = "0";
10130                     a = {width: zero};
10131                 break;
10132                 case "r":
10133                     st.left = st.top = "0";
10134                     a = {width: zero, points: {to:[b.right, b.y]}};
10135                 break;
10136                 case "b":
10137                     st.left = st.top = "0";
10138                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10139                 break;
10140                 case "tl":
10141                     st.right = st.bottom = "0";
10142                     a = {width: zero, height: zero};
10143                 break;
10144                 case "bl":
10145                     st.right = st.top = "0";
10146                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "br":
10149                     st.left = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10151                 break;
10152                 case "tr":
10153                     st.left = st.bottom = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10155                 break;
10156             }
10157
10158             arguments.callee.anim = wrap.fxanim(a,
10159                 o,
10160                 'motion',
10161                 .5,
10162                 "easeOut", after);
10163         });
10164         return this;
10165     },
10166
10167         /**
10168          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10169          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10170          * The element must be removed from the DOM using the 'remove' config option if desired.
10171          * Usage:
10172          *<pre><code>
10173 // default
10174 el.puff();
10175
10176 // common config options shown with default values
10177 el.puff({
10178     easing: 'easeOut',
10179     duration: .5,
10180     remove: false,
10181     useDisplay: false
10182 });
10183 </code></pre>
10184          * @param {Object} options (optional) Object literal with any of the Fx config options
10185          * @return {Roo.Element} The Element
10186          */
10187     puff : function(o){
10188         var el = this.getFxEl();
10189         o = o || {};
10190
10191         el.queueFx(o, function(){
10192             this.clearOpacity();
10193             this.show();
10194
10195             // restore values after effect
10196             var r = this.getFxRestore();
10197             var st = this.dom.style;
10198
10199             var after = function(){
10200                 if(o.useDisplay){
10201                     el.setDisplayed(false);
10202                 }else{
10203                     el.hide();
10204                 }
10205
10206                 el.clearOpacity();
10207
10208                 el.setPositioning(r.pos);
10209                 st.width = r.width;
10210                 st.height = r.height;
10211                 st.fontSize = '';
10212                 el.afterFx(o);
10213             };
10214
10215             var width = this.getWidth();
10216             var height = this.getHeight();
10217
10218             arguments.callee.anim = this.fxanim({
10219                     width : {to: this.adjustWidth(width * 2)},
10220                     height : {to: this.adjustHeight(height * 2)},
10221                     points : {by: [-(width * .5), -(height * .5)]},
10222                     opacity : {to: 0},
10223                     fontSize: {to:200, unit: "%"}
10224                 },
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10235          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10236          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.switchOff();
10241
10242 // all config options shown with default values
10243 el.switchOff({
10244     easing: 'easeIn',
10245     duration: .3,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     switchOff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.clip();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273                 el.setPositioning(r.pos);
10274                 st.width = r.width;
10275                 st.height = r.height;
10276
10277                 el.afterFx(o);
10278             };
10279
10280             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10281                 this.clearOpacity();
10282                 (function(){
10283                     this.fxanim({
10284                         height:{to:1},
10285                         points:{by:[0, this.getHeight() * .5]}
10286                     }, o, 'motion', 0.3, 'easeIn', after);
10287                 }).defer(100, this);
10288             });
10289         });
10290         return this;
10291     },
10292
10293     /**
10294      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10295      * changed using the "attr" config option) and then fading back to the original color. If no original
10296      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10297      * Usage:
10298 <pre><code>
10299 // default: highlight background to yellow
10300 el.highlight();
10301
10302 // custom: highlight foreground text to blue for 2 seconds
10303 el.highlight("0000ff", { attr: 'color', duration: 2 });
10304
10305 // common config options shown with default values
10306 el.highlight("ffff9c", {
10307     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10308     endColor: (current color) or "ffffff",
10309     easing: 'easeIn',
10310     duration: 1
10311 });
10312 </code></pre>
10313      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10314      * @param {Object} options (optional) Object literal with any of the Fx config options
10315      * @return {Roo.Element} The Element
10316      */ 
10317     highlight : function(color, o){
10318         var el = this.getFxEl();
10319         o = o || {};
10320
10321         el.queueFx(o, function(){
10322             color = color || "ffff9c";
10323             attr = o.attr || "backgroundColor";
10324
10325             this.clearOpacity();
10326             this.show();
10327
10328             var origColor = this.getColor(attr);
10329             var restoreColor = this.dom.style[attr];
10330             endColor = (o.endColor || origColor) || "ffffff";
10331
10332             var after = function(){
10333                 el.dom.style[attr] = restoreColor;
10334                 el.afterFx(o);
10335             };
10336
10337             var a = {};
10338             a[attr] = {from: color, to: endColor};
10339             arguments.callee.anim = this.fxanim(a,
10340                 o,
10341                 'color',
10342                 1,
10343                 'easeIn', after);
10344         });
10345         return this;
10346     },
10347
10348    /**
10349     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10350     * Usage:
10351 <pre><code>
10352 // default: a single light blue ripple
10353 el.frame();
10354
10355 // custom: 3 red ripples lasting 3 seconds total
10356 el.frame("ff0000", 3, { duration: 3 });
10357
10358 // common config options shown with default values
10359 el.frame("C3DAF9", 1, {
10360     duration: 1 //duration of entire animation (not each individual ripple)
10361     // Note: Easing is not configurable and will be ignored if included
10362 });
10363 </code></pre>
10364     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10365     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10366     * @param {Object} options (optional) Object literal with any of the Fx config options
10367     * @return {Roo.Element} The Element
10368     */
10369     frame : function(color, count, o){
10370         var el = this.getFxEl();
10371         o = o || {};
10372
10373         el.queueFx(o, function(){
10374             color = color || "#C3DAF9";
10375             if(color.length == 6){
10376                 color = "#" + color;
10377             }
10378             count = count || 1;
10379             duration = o.duration || 1;
10380             this.show();
10381
10382             var b = this.getBox();
10383             var animFn = function(){
10384                 var proxy = this.createProxy({
10385
10386                      style:{
10387                         visbility:"hidden",
10388                         position:"absolute",
10389                         "z-index":"35000", // yee haw
10390                         border:"0px solid " + color
10391                      }
10392                   });
10393                 var scale = Roo.isBorderBox ? 2 : 1;
10394                 proxy.animate({
10395                     top:{from:b.y, to:b.y - 20},
10396                     left:{from:b.x, to:b.x - 20},
10397                     borderWidth:{from:0, to:10},
10398                     opacity:{from:1, to:0},
10399                     height:{from:b.height, to:(b.height + (20*scale))},
10400                     width:{from:b.width, to:(b.width + (20*scale))}
10401                 }, duration, function(){
10402                     proxy.remove();
10403                 });
10404                 if(--count > 0){
10405                      animFn.defer((duration/2)*1000, this);
10406                 }else{
10407                     el.afterFx(o);
10408                 }
10409             };
10410             animFn.call(this);
10411         });
10412         return this;
10413     },
10414
10415    /**
10416     * Creates a pause before any subsequent queued effects begin.  If there are
10417     * no effects queued after the pause it will have no effect.
10418     * Usage:
10419 <pre><code>
10420 el.pause(1);
10421 </code></pre>
10422     * @param {Number} seconds The length of time to pause (in seconds)
10423     * @return {Roo.Element} The Element
10424     */
10425     pause : function(seconds){
10426         var el = this.getFxEl();
10427         var o = {};
10428
10429         el.queueFx(o, function(){
10430             setTimeout(function(){
10431                 el.afterFx(o);
10432             }, seconds * 1000);
10433         });
10434         return this;
10435     },
10436
10437    /**
10438     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10439     * using the "endOpacity" config option.
10440     * Usage:
10441 <pre><code>
10442 // default: fade in from opacity 0 to 100%
10443 el.fadeIn();
10444
10445 // custom: fade in from opacity 0 to 75% over 2 seconds
10446 el.fadeIn({ endOpacity: .75, duration: 2});
10447
10448 // common config options shown with default values
10449 el.fadeIn({
10450     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10451     easing: 'easeOut',
10452     duration: .5
10453 });
10454 </code></pre>
10455     * @param {Object} options (optional) Object literal with any of the Fx config options
10456     * @return {Roo.Element} The Element
10457     */
10458     fadeIn : function(o){
10459         var el = this.getFxEl();
10460         o = o || {};
10461         el.queueFx(o, function(){
10462             this.setOpacity(0);
10463             this.fixDisplay();
10464             this.dom.style.visibility = 'visible';
10465             var to = o.endOpacity || 1;
10466             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10467                 o, null, .5, "easeOut", function(){
10468                 if(to == 1){
10469                     this.clearOpacity();
10470                 }
10471                 el.afterFx(o);
10472             });
10473         });
10474         return this;
10475     },
10476
10477    /**
10478     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10479     * using the "endOpacity" config option.
10480     * Usage:
10481 <pre><code>
10482 // default: fade out from the element's current opacity to 0
10483 el.fadeOut();
10484
10485 // custom: fade out from the element's current opacity to 25% over 2 seconds
10486 el.fadeOut({ endOpacity: .25, duration: 2});
10487
10488 // common config options shown with default values
10489 el.fadeOut({
10490     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10491     easing: 'easeOut',
10492     duration: .5
10493     remove: false,
10494     useDisplay: false
10495 });
10496 </code></pre>
10497     * @param {Object} options (optional) Object literal with any of the Fx config options
10498     * @return {Roo.Element} The Element
10499     */
10500     fadeOut : function(o){
10501         var el = this.getFxEl();
10502         o = o || {};
10503         el.queueFx(o, function(){
10504             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10505                 o, null, .5, "easeOut", function(){
10506                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10507                      this.dom.style.display = "none";
10508                 }else{
10509                      this.dom.style.visibility = "hidden";
10510                 }
10511                 this.clearOpacity();
10512                 el.afterFx(o);
10513             });
10514         });
10515         return this;
10516     },
10517
10518    /**
10519     * Animates the transition of an element's dimensions from a starting height/width
10520     * to an ending height/width.
10521     * Usage:
10522 <pre><code>
10523 // change height and width to 100x100 pixels
10524 el.scale(100, 100);
10525
10526 // common config options shown with default values.  The height and width will default to
10527 // the element's existing values if passed as null.
10528 el.scale(
10529     [element's width],
10530     [element's height], {
10531     easing: 'easeOut',
10532     duration: .35
10533 });
10534 </code></pre>
10535     * @param {Number} width  The new width (pass undefined to keep the original width)
10536     * @param {Number} height  The new height (pass undefined to keep the original height)
10537     * @param {Object} options (optional) Object literal with any of the Fx config options
10538     * @return {Roo.Element} The Element
10539     */
10540     scale : function(w, h, o){
10541         this.shift(Roo.apply({}, o, {
10542             width: w,
10543             height: h
10544         }));
10545         return this;
10546     },
10547
10548    /**
10549     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10550     * Any of these properties not specified in the config object will not be changed.  This effect 
10551     * requires that at least one new dimension, position or opacity setting must be passed in on
10552     * the config object in order for the function to have any effect.
10553     * Usage:
10554 <pre><code>
10555 // slide the element horizontally to x position 200 while changing the height and opacity
10556 el.shift({ x: 200, height: 50, opacity: .8 });
10557
10558 // common config options shown with default values.
10559 el.shift({
10560     width: [element's width],
10561     height: [element's height],
10562     x: [element's x position],
10563     y: [element's y position],
10564     opacity: [element's opacity],
10565     easing: 'easeOut',
10566     duration: .35
10567 });
10568 </code></pre>
10569     * @param {Object} options  Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     shift : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10577             if(w !== undefined){
10578                 a.width = {to: this.adjustWidth(w)};
10579             }
10580             if(h !== undefined){
10581                 a.height = {to: this.adjustHeight(h)};
10582             }
10583             if(x !== undefined || y !== undefined){
10584                 a.points = {to: [
10585                     x !== undefined ? x : this.getX(),
10586                     y !== undefined ? y : this.getY()
10587                 ]};
10588             }
10589             if(op !== undefined){
10590                 a.opacity = {to: op};
10591             }
10592             if(o.xy !== undefined){
10593                 a.points = {to: o.xy};
10594             }
10595             arguments.callee.anim = this.fxanim(a,
10596                 o, 'motion', .35, "easeOut", function(){
10597                 el.afterFx(o);
10598             });
10599         });
10600         return this;
10601     },
10602
10603         /**
10604          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10605          * ending point of the effect.
10606          * Usage:
10607          *<pre><code>
10608 // default: slide the element downward while fading out
10609 el.ghost();
10610
10611 // custom: slide the element out to the right with a 2-second duration
10612 el.ghost('r', { duration: 2 });
10613
10614 // common config options shown with default values
10615 el.ghost('b', {
10616     easing: 'easeOut',
10617     duration: .5
10618     remove: false,
10619     useDisplay: false
10620 });
10621 </code></pre>
10622          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10623          * @param {Object} options (optional) Object literal with any of the Fx config options
10624          * @return {Roo.Element} The Element
10625          */
10626     ghost : function(anchor, o){
10627         var el = this.getFxEl();
10628         o = o || {};
10629
10630         el.queueFx(o, function(){
10631             anchor = anchor || "b";
10632
10633             // restore values after effect
10634             var r = this.getFxRestore();
10635             var w = this.getWidth(),
10636                 h = this.getHeight();
10637
10638             var st = this.dom.style;
10639
10640             var after = function(){
10641                 if(o.useDisplay){
10642                     el.setDisplayed(false);
10643                 }else{
10644                     el.hide();
10645                 }
10646
10647                 el.clearOpacity();
10648                 el.setPositioning(r.pos);
10649                 st.width = r.width;
10650                 st.height = r.height;
10651
10652                 el.afterFx(o);
10653             };
10654
10655             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10656             switch(anchor.toLowerCase()){
10657                 case "t":
10658                     pt.by = [0, -h];
10659                 break;
10660                 case "l":
10661                     pt.by = [-w, 0];
10662                 break;
10663                 case "r":
10664                     pt.by = [w, 0];
10665                 break;
10666                 case "b":
10667                     pt.by = [0, h];
10668                 break;
10669                 case "tl":
10670                     pt.by = [-w, -h];
10671                 break;
10672                 case "bl":
10673                     pt.by = [-w, h];
10674                 break;
10675                 case "br":
10676                     pt.by = [w, h];
10677                 break;
10678                 case "tr":
10679                     pt.by = [w, -h];
10680                 break;
10681             }
10682
10683             arguments.callee.anim = this.fxanim(a,
10684                 o,
10685                 'motion',
10686                 .5,
10687                 "easeOut", after);
10688         });
10689         return this;
10690     },
10691
10692         /**
10693          * Ensures that all effects queued after syncFx is called on the element are
10694          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10695          * @return {Roo.Element} The Element
10696          */
10697     syncFx : function(){
10698         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10699             block : false,
10700             concurrent : true,
10701             stopFx : false
10702         });
10703         return this;
10704     },
10705
10706         /**
10707          * Ensures that all effects queued after sequenceFx is called on the element are
10708          * run in sequence.  This is the opposite of {@link #syncFx}.
10709          * @return {Roo.Element} The Element
10710          */
10711     sequenceFx : function(){
10712         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10713             block : false,
10714             concurrent : false,
10715             stopFx : false
10716         });
10717         return this;
10718     },
10719
10720         /* @private */
10721     nextFx : function(){
10722         var ef = this.fxQueue[0];
10723         if(ef){
10724             ef.call(this);
10725         }
10726     },
10727
10728         /**
10729          * Returns true if the element has any effects actively running or queued, else returns false.
10730          * @return {Boolean} True if element has active effects, else false
10731          */
10732     hasActiveFx : function(){
10733         return this.fxQueue && this.fxQueue[0];
10734     },
10735
10736         /**
10737          * Stops any running effects and clears the element's internal effects queue if it contains
10738          * any additional effects that haven't started yet.
10739          * @return {Roo.Element} The Element
10740          */
10741     stopFx : function(){
10742         if(this.hasActiveFx()){
10743             var cur = this.fxQueue[0];
10744             if(cur && cur.anim && cur.anim.isAnimated()){
10745                 this.fxQueue = [cur]; // clear out others
10746                 cur.anim.stop(true);
10747             }
10748         }
10749         return this;
10750     },
10751
10752         /* @private */
10753     beforeFx : function(o){
10754         if(this.hasActiveFx() && !o.concurrent){
10755            if(o.stopFx){
10756                this.stopFx();
10757                return true;
10758            }
10759            return false;
10760         }
10761         return true;
10762     },
10763
10764         /**
10765          * Returns true if the element is currently blocking so that no other effect can be queued
10766          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10767          * used to ensure that an effect initiated by a user action runs to completion prior to the
10768          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10769          * @return {Boolean} True if blocking, else false
10770          */
10771     hasFxBlock : function(){
10772         var q = this.fxQueue;
10773         return q && q[0] && q[0].block;
10774     },
10775
10776         /* @private */
10777     queueFx : function(o, fn){
10778         if(!this.fxQueue){
10779             this.fxQueue = [];
10780         }
10781         if(!this.hasFxBlock()){
10782             Roo.applyIf(o, this.fxDefaults);
10783             if(!o.concurrent){
10784                 var run = this.beforeFx(o);
10785                 fn.block = o.block;
10786                 this.fxQueue.push(fn);
10787                 if(run){
10788                     this.nextFx();
10789                 }
10790             }else{
10791                 fn.call(this);
10792             }
10793         }
10794         return this;
10795     },
10796
10797         /* @private */
10798     fxWrap : function(pos, o, vis){
10799         var wrap;
10800         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10801             var wrapXY;
10802             if(o.fixPosition){
10803                 wrapXY = this.getXY();
10804             }
10805             var div = document.createElement("div");
10806             div.style.visibility = vis;
10807             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10808             wrap.setPositioning(pos);
10809             if(wrap.getStyle("position") == "static"){
10810                 wrap.position("relative");
10811             }
10812             this.clearPositioning('auto');
10813             wrap.clip();
10814             wrap.dom.appendChild(this.dom);
10815             if(wrapXY){
10816                 wrap.setXY(wrapXY);
10817             }
10818         }
10819         return wrap;
10820     },
10821
10822         /* @private */
10823     fxUnwrap : function(wrap, pos, o){
10824         this.clearPositioning();
10825         this.setPositioning(pos);
10826         if(!o.wrap){
10827             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10828             wrap.remove();
10829         }
10830     },
10831
10832         /* @private */
10833     getFxRestore : function(){
10834         var st = this.dom.style;
10835         return {pos: this.getPositioning(), width: st.width, height : st.height};
10836     },
10837
10838         /* @private */
10839     afterFx : function(o){
10840         if(o.afterStyle){
10841             this.applyStyles(o.afterStyle);
10842         }
10843         if(o.afterCls){
10844             this.addClass(o.afterCls);
10845         }
10846         if(o.remove === true){
10847             this.remove();
10848         }
10849         Roo.callback(o.callback, o.scope, [this]);
10850         if(!o.concurrent){
10851             this.fxQueue.shift();
10852             this.nextFx();
10853         }
10854     },
10855
10856         /* @private */
10857     getFxEl : function(){ // support for composite element fx
10858         return Roo.get(this.dom);
10859     },
10860
10861         /* @private */
10862     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10863         animType = animType || 'run';
10864         opt = opt || {};
10865         var anim = Roo.lib.Anim[animType](
10866             this.dom, args,
10867             (opt.duration || defaultDur) || .35,
10868             (opt.easing || defaultEase) || 'easeOut',
10869             function(){
10870                 Roo.callback(cb, this);
10871             },
10872             this
10873         );
10874         opt.anim = anim;
10875         return anim;
10876     }
10877 };
10878
10879 // backwords compat
10880 Roo.Fx.resize = Roo.Fx.scale;
10881
10882 //When included, Roo.Fx is automatically applied to Element so that all basic
10883 //effects are available directly via the Element API
10884 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10885  * Based on:
10886  * Ext JS Library 1.1.1
10887  * Copyright(c) 2006-2007, Ext JS, LLC.
10888  *
10889  * Originally Released Under LGPL - original licence link has changed is not relivant.
10890  *
10891  * Fork - LGPL
10892  * <script type="text/javascript">
10893  */
10894
10895
10896 /**
10897  * @class Roo.CompositeElement
10898  * Standard composite class. Creates a Roo.Element for every element in the collection.
10899  * <br><br>
10900  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10901  * actions will be performed on all the elements in this collection.</b>
10902  * <br><br>
10903  * All methods return <i>this</i> and can be chained.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class", true);
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class', true);
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre>
10915  */
10916 Roo.CompositeElement = function(els){
10917     this.elements = [];
10918     this.addElements(els);
10919 };
10920 Roo.CompositeElement.prototype = {
10921     isComposite: true,
10922     addElements : function(els){
10923         if(!els) return this;
10924         if(typeof els == "string"){
10925             els = Roo.Element.selectorFunction(els);
10926         }
10927         var yels = this.elements;
10928         var index = yels.length-1;
10929         for(var i = 0, len = els.length; i < len; i++) {
10930                 yels[++index] = Roo.get(els[i]);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Clears this composite and adds the elements returned by the passed selector.
10937     * @param {String/Array} els A string CSS selector, an array of elements or an element
10938     * @return {CompositeElement} this
10939     */
10940     fill : function(els){
10941         this.elements = [];
10942         this.add(els);
10943         return this;
10944     },
10945
10946     /**
10947     * Filters this composite to only elements that match the passed selector.
10948     * @param {String} selector A string CSS selector
10949     * @param {Boolean} inverse return inverse filter (not matches)
10950     * @return {CompositeElement} this
10951     */
10952     filter : function(selector, inverse){
10953         var els = [];
10954         inverse = inverse || false;
10955         this.each(function(el){
10956             var match = inverse ? !el.is(selector) : el.is(selector);
10957             if(match){
10958                 els[els.length] = el.dom;
10959             }
10960         });
10961         this.fill(els);
10962         return this;
10963     },
10964
10965     invoke : function(fn, args){
10966         var els = this.elements;
10967         for(var i = 0, len = els.length; i < len; i++) {
10968                 Roo.Element.prototype[fn].apply(els[i], args);
10969         }
10970         return this;
10971     },
10972     /**
10973     * Adds elements to this composite.
10974     * @param {String/Array} els A string CSS selector, an array of elements or an element
10975     * @return {CompositeElement} this
10976     */
10977     add : function(els){
10978         if(typeof els == "string"){
10979             this.addElements(Roo.Element.selectorFunction(els));
10980         }else if(els.length !== undefined){
10981             this.addElements(els);
10982         }else{
10983             this.addElements([els]);
10984         }
10985         return this;
10986     },
10987     /**
10988     * Calls the passed function passing (el, this, index) for each element in this composite.
10989     * @param {Function} fn The function to call
10990     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10991     * @return {CompositeElement} this
10992     */
10993     each : function(fn, scope){
10994         var els = this.elements;
10995         for(var i = 0, len = els.length; i < len; i++){
10996             if(fn.call(scope || els[i], els[i], this, i) === false) {
10997                 break;
10998             }
10999         }
11000         return this;
11001     },
11002
11003     /**
11004      * Returns the Element object at the specified index
11005      * @param {Number} index
11006      * @return {Roo.Element}
11007      */
11008     item : function(index){
11009         return this.elements[index] || null;
11010     },
11011
11012     /**
11013      * Returns the first Element
11014      * @return {Roo.Element}
11015      */
11016     first : function(){
11017         return this.item(0);
11018     },
11019
11020     /**
11021      * Returns the last Element
11022      * @return {Roo.Element}
11023      */
11024     last : function(){
11025         return this.item(this.elements.length-1);
11026     },
11027
11028     /**
11029      * Returns the number of elements in this composite
11030      * @return Number
11031      */
11032     getCount : function(){
11033         return this.elements.length;
11034     },
11035
11036     /**
11037      * Returns true if this composite contains the passed element
11038      * @return Boolean
11039      */
11040     contains : function(el){
11041         return this.indexOf(el) !== -1;
11042     },
11043
11044     /**
11045      * Returns true if this composite contains the passed element
11046      * @return Boolean
11047      */
11048     indexOf : function(el){
11049         return this.elements.indexOf(Roo.get(el));
11050     },
11051
11052
11053     /**
11054     * Removes the specified element(s).
11055     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11056     * or an array of any of those.
11057     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11058     * @return {CompositeElement} this
11059     */
11060     removeElement : function(el, removeDom){
11061         if(el instanceof Array){
11062             for(var i = 0, len = el.length; i < len; i++){
11063                 this.removeElement(el[i]);
11064             }
11065             return this;
11066         }
11067         var index = typeof el == 'number' ? el : this.indexOf(el);
11068         if(index !== -1){
11069             if(removeDom){
11070                 var d = this.elements[index];
11071                 if(d.dom){
11072                     d.remove();
11073                 }else{
11074                     d.parentNode.removeChild(d);
11075                 }
11076             }
11077             this.elements.splice(index, 1);
11078         }
11079         return this;
11080     },
11081
11082     /**
11083     * Replaces the specified element with the passed element.
11084     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11085     * to replace.
11086     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11087     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11088     * @return {CompositeElement} this
11089     */
11090     replaceElement : function(el, replacement, domReplace){
11091         var index = typeof el == 'number' ? el : this.indexOf(el);
11092         if(index !== -1){
11093             if(domReplace){
11094                 this.elements[index].replaceWith(replacement);
11095             }else{
11096                 this.elements.splice(index, 1, Roo.get(replacement))
11097             }
11098         }
11099         return this;
11100     },
11101
11102     /**
11103      * Removes all elements.
11104      */
11105     clear : function(){
11106         this.elements = [];
11107     }
11108 };
11109 (function(){
11110     Roo.CompositeElement.createCall = function(proto, fnName){
11111         if(!proto[fnName]){
11112             proto[fnName] = function(){
11113                 return this.invoke(fnName, arguments);
11114             };
11115         }
11116     };
11117     for(var fnName in Roo.Element.prototype){
11118         if(typeof Roo.Element.prototype[fnName] == "function"){
11119             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11120         }
11121     };
11122 })();
11123 /*
11124  * Based on:
11125  * Ext JS Library 1.1.1
11126  * Copyright(c) 2006-2007, Ext JS, LLC.
11127  *
11128  * Originally Released Under LGPL - original licence link has changed is not relivant.
11129  *
11130  * Fork - LGPL
11131  * <script type="text/javascript">
11132  */
11133
11134 /**
11135  * @class Roo.CompositeElementLite
11136  * @extends Roo.CompositeElement
11137  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11138  <pre><code>
11139  var els = Roo.select("#some-el div.some-class");
11140  // or select directly from an existing element
11141  var el = Roo.get('some-el');
11142  el.select('div.some-class');
11143
11144  els.setWidth(100); // all elements become 100 width
11145  els.hide(true); // all elements fade out and hide
11146  // or
11147  els.setWidth(100).hide(true);
11148  </code></pre><br><br>
11149  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11150  * actions will be performed on all the elements in this collection.</b>
11151  */
11152 Roo.CompositeElementLite = function(els){
11153     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11154     this.el = new Roo.Element.Flyweight();
11155 };
11156 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11157     addElements : function(els){
11158         if(els){
11159             if(els instanceof Array){
11160                 this.elements = this.elements.concat(els);
11161             }else{
11162                 var yels = this.elements;
11163                 var index = yels.length-1;
11164                 for(var i = 0, len = els.length; i < len; i++) {
11165                     yels[++index] = els[i];
11166                 }
11167             }
11168         }
11169         return this;
11170     },
11171     invoke : function(fn, args){
11172         var els = this.elements;
11173         var el = this.el;
11174         for(var i = 0, len = els.length; i < len; i++) {
11175             el.dom = els[i];
11176                 Roo.Element.prototype[fn].apply(el, args);
11177         }
11178         return this;
11179     },
11180     /**
11181      * Returns a flyweight Element of the dom element object at the specified index
11182      * @param {Number} index
11183      * @return {Roo.Element}
11184      */
11185     item : function(index){
11186         if(!this.elements[index]){
11187             return null;
11188         }
11189         this.el.dom = this.elements[index];
11190         return this.el;
11191     },
11192
11193     // fixes scope with flyweight
11194     addListener : function(eventName, handler, scope, opt){
11195         var els = this.elements;
11196         for(var i = 0, len = els.length; i < len; i++) {
11197             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11198         }
11199         return this;
11200     },
11201
11202     /**
11203     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11204     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11205     * a reference to the dom node, use el.dom.</b>
11206     * @param {Function} fn The function to call
11207     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11208     * @return {CompositeElement} this
11209     */
11210     each : function(fn, scope){
11211         var els = this.elements;
11212         var el = this.el;
11213         for(var i = 0, len = els.length; i < len; i++){
11214             el.dom = els[i];
11215                 if(fn.call(scope || el, el, this, i) === false){
11216                 break;
11217             }
11218         }
11219         return this;
11220     },
11221
11222     indexOf : function(el){
11223         return this.elements.indexOf(Roo.getDom(el));
11224     },
11225
11226     replaceElement : function(el, replacement, domReplace){
11227         var index = typeof el == 'number' ? el : this.indexOf(el);
11228         if(index !== -1){
11229             replacement = Roo.getDom(replacement);
11230             if(domReplace){
11231                 var d = this.elements[index];
11232                 d.parentNode.insertBefore(replacement, d);
11233                 d.parentNode.removeChild(d);
11234             }
11235             this.elements.splice(index, 1, replacement);
11236         }
11237         return this;
11238     }
11239 });
11240 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11241
11242 /*
11243  * Based on:
11244  * Ext JS Library 1.1.1
11245  * Copyright(c) 2006-2007, Ext JS, LLC.
11246  *
11247  * Originally Released Under LGPL - original licence link has changed is not relivant.
11248  *
11249  * Fork - LGPL
11250  * <script type="text/javascript">
11251  */
11252
11253  
11254
11255 /**
11256  * @class Roo.data.Connection
11257  * @extends Roo.util.Observable
11258  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11259  * either to a configured URL, or to a URL specified at request time.<br><br>
11260  * <p>
11261  * Requests made by this class are asynchronous, and will return immediately. No data from
11262  * the server will be available to the statement immediately following the {@link #request} call.
11263  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11264  * <p>
11265  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11266  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11267  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11268  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11269  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11270  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11271  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11272  * standard DOM methods.
11273  * @constructor
11274  * @param {Object} config a configuration object.
11275  */
11276 Roo.data.Connection = function(config){
11277     Roo.apply(this, config);
11278     this.addEvents({
11279         /**
11280          * @event beforerequest
11281          * Fires before a network request is made to retrieve a data object.
11282          * @param {Connection} conn This Connection object.
11283          * @param {Object} options The options config object passed to the {@link #request} method.
11284          */
11285         "beforerequest" : true,
11286         /**
11287          * @event requestcomplete
11288          * Fires if the request was successfully completed.
11289          * @param {Connection} conn This Connection object.
11290          * @param {Object} response The XHR object containing the response data.
11291          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11292          * @param {Object} options The options config object passed to the {@link #request} method.
11293          */
11294         "requestcomplete" : true,
11295         /**
11296          * @event requestexception
11297          * Fires if an error HTTP status was returned from the server.
11298          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11299          * @param {Connection} conn This Connection object.
11300          * @param {Object} response The XHR object containing the response data.
11301          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11302          * @param {Object} options The options config object passed to the {@link #request} method.
11303          */
11304         "requestexception" : true
11305     });
11306     Roo.data.Connection.superclass.constructor.call(this);
11307 };
11308
11309 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11310     /**
11311      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11312      */
11313     /**
11314      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11315      * extra parameters to each request made by this object. (defaults to undefined)
11316      */
11317     /**
11318      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11319      *  to each request made by this object. (defaults to undefined)
11320      */
11321     /**
11322      * @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)
11323      */
11324     /**
11325      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11326      */
11327     timeout : 30000,
11328     /**
11329      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11330      * @type Boolean
11331      */
11332     autoAbort:false,
11333
11334     /**
11335      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11336      * @type Boolean
11337      */
11338     disableCaching: true,
11339
11340     /**
11341      * Sends an HTTP request to a remote server.
11342      * @param {Object} options An object which may contain the following properties:<ul>
11343      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11344      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11345      * request, a url encoded string or a function to call to get either.</li>
11346      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11347      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11348      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11349      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11350      * <li>options {Object} The parameter to the request call.</li>
11351      * <li>success {Boolean} True if the request succeeded.</li>
11352      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11353      * </ul></li>
11354      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11355      * The callback is passed the following parameters:<ul>
11356      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11357      * <li>options {Object} The parameter to the request call.</li>
11358      * </ul></li>
11359      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11360      * The callback is passed the following parameters:<ul>
11361      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11362      * <li>options {Object} The parameter to the request call.</li>
11363      * </ul></li>
11364      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11365      * for the callback function. Defaults to the browser window.</li>
11366      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11367      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11368      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11369      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11370      * params for the post data. Any params will be appended to the URL.</li>
11371      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11372      * </ul>
11373      * @return {Number} transactionId
11374      */
11375     request : function(o){
11376         if(this.fireEvent("beforerequest", this, o) !== false){
11377             var p = o.params;
11378
11379             if(typeof p == "function"){
11380                 p = p.call(o.scope||window, o);
11381             }
11382             if(typeof p == "object"){
11383                 p = Roo.urlEncode(o.params);
11384             }
11385             if(this.extraParams){
11386                 var extras = Roo.urlEncode(this.extraParams);
11387                 p = p ? (p + '&' + extras) : extras;
11388             }
11389
11390             var url = o.url || this.url;
11391             if(typeof url == 'function'){
11392                 url = url.call(o.scope||window, o);
11393             }
11394
11395             if(o.form){
11396                 var form = Roo.getDom(o.form);
11397                 url = url || form.action;
11398
11399                 var enctype = form.getAttribute("enctype");
11400                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11401                     return this.doFormUpload(o, p, url);
11402                 }
11403                 var f = Roo.lib.Ajax.serializeForm(form);
11404                 p = p ? (p + '&' + f) : f;
11405             }
11406
11407             var hs = o.headers;
11408             if(this.defaultHeaders){
11409                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11410                 if(!o.headers){
11411                     o.headers = hs;
11412                 }
11413             }
11414
11415             var cb = {
11416                 success: this.handleResponse,
11417                 failure: this.handleFailure,
11418                 scope: this,
11419                 argument: {options: o},
11420                 timeout : o.timeout || this.timeout
11421             };
11422
11423             var method = o.method||this.method||(p ? "POST" : "GET");
11424
11425             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11426                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11427             }
11428
11429             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11430                 if(o.autoAbort){
11431                     this.abort();
11432                 }
11433             }else if(this.autoAbort !== false){
11434                 this.abort();
11435             }
11436
11437             if((method == 'GET' && p) || o.xmlData){
11438                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11439                 p = '';
11440             }
11441             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11442             return this.transId;
11443         }else{
11444             Roo.callback(o.callback, o.scope, [o, null, null]);
11445             return null;
11446         }
11447     },
11448
11449     /**
11450      * Determine whether this object has a request outstanding.
11451      * @param {Number} transactionId (Optional) defaults to the last transaction
11452      * @return {Boolean} True if there is an outstanding request.
11453      */
11454     isLoading : function(transId){
11455         if(transId){
11456             return Roo.lib.Ajax.isCallInProgress(transId);
11457         }else{
11458             return this.transId ? true : false;
11459         }
11460     },
11461
11462     /**
11463      * Aborts any outstanding request.
11464      * @param {Number} transactionId (Optional) defaults to the last transaction
11465      */
11466     abort : function(transId){
11467         if(transId || this.isLoading()){
11468             Roo.lib.Ajax.abort(transId || this.transId);
11469         }
11470     },
11471
11472     // private
11473     handleResponse : function(response){
11474         this.transId = false;
11475         var options = response.argument.options;
11476         response.argument = options ? options.argument : null;
11477         this.fireEvent("requestcomplete", this, response, options);
11478         Roo.callback(options.success, options.scope, [response, options]);
11479         Roo.callback(options.callback, options.scope, [options, true, response]);
11480     },
11481
11482     // private
11483     handleFailure : function(response, e){
11484         this.transId = false;
11485         var options = response.argument.options;
11486         response.argument = options ? options.argument : null;
11487         this.fireEvent("requestexception", this, response, options, e);
11488         Roo.callback(options.failure, options.scope, [response, options]);
11489         Roo.callback(options.callback, options.scope, [options, false, response]);
11490     },
11491
11492     // private
11493     doFormUpload : function(o, ps, url){
11494         var id = Roo.id();
11495         var frame = document.createElement('iframe');
11496         frame.id = id;
11497         frame.name = id;
11498         frame.className = 'x-hidden';
11499         if(Roo.isIE){
11500             frame.src = Roo.SSL_SECURE_URL;
11501         }
11502         document.body.appendChild(frame);
11503
11504         if(Roo.isIE){
11505            document.frames[id].name = id;
11506         }
11507
11508         var form = Roo.getDom(o.form);
11509         form.target = id;
11510         form.method = 'POST';
11511         form.enctype = form.encoding = 'multipart/form-data';
11512         if(url){
11513             form.action = url;
11514         }
11515
11516         var hiddens, hd;
11517         if(ps){ // add dynamic params
11518             hiddens = [];
11519             ps = Roo.urlDecode(ps, false);
11520             for(var k in ps){
11521                 if(ps.hasOwnProperty(k)){
11522                     hd = document.createElement('input');
11523                     hd.type = 'hidden';
11524                     hd.name = k;
11525                     hd.value = ps[k];
11526                     form.appendChild(hd);
11527                     hiddens.push(hd);
11528                 }
11529             }
11530         }
11531
11532         function cb(){
11533             var r = {  // bogus response object
11534                 responseText : '',
11535                 responseXML : null
11536             };
11537
11538             r.argument = o ? o.argument : null;
11539
11540             try { //
11541                 var doc;
11542                 if(Roo.isIE){
11543                     doc = frame.contentWindow.document;
11544                 }else {
11545                     doc = (frame.contentDocument || window.frames[id].document);
11546                 }
11547                 if(doc && doc.body){
11548                     r.responseText = doc.body.innerHTML;
11549                 }
11550                 if(doc && doc.XMLDocument){
11551                     r.responseXML = doc.XMLDocument;
11552                 }else {
11553                     r.responseXML = doc;
11554                 }
11555             }
11556             catch(e) {
11557                 // ignore
11558             }
11559
11560             Roo.EventManager.removeListener(frame, 'load', cb, this);
11561
11562             this.fireEvent("requestcomplete", this, r, o);
11563             Roo.callback(o.success, o.scope, [r, o]);
11564             Roo.callback(o.callback, o.scope, [o, true, r]);
11565
11566             setTimeout(function(){document.body.removeChild(frame);}, 100);
11567         }
11568
11569         Roo.EventManager.on(frame, 'load', cb, this);
11570         form.submit();
11571
11572         if(hiddens){ // remove dynamic params
11573             for(var i = 0, len = hiddens.length; i < len; i++){
11574                 form.removeChild(hiddens[i]);
11575             }
11576         }
11577     }
11578 });
11579 /*
11580  * Based on:
11581  * Ext JS Library 1.1.1
11582  * Copyright(c) 2006-2007, Ext JS, LLC.
11583  *
11584  * Originally Released Under LGPL - original licence link has changed is not relivant.
11585  *
11586  * Fork - LGPL
11587  * <script type="text/javascript">
11588  */
11589  
11590 /**
11591  * Global Ajax request class.
11592  * 
11593  * @class Roo.Ajax
11594  * @extends Roo.data.Connection
11595  * @static
11596  * 
11597  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11598  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11599  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11600  * @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)
11601  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11602  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11603  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11604  */
11605 Roo.Ajax = new Roo.data.Connection({
11606     // fix up the docs
11607     /**
11608      * @scope Roo.Ajax
11609      * @type {Boolear} 
11610      */
11611     autoAbort : false,
11612
11613     /**
11614      * Serialize the passed form into a url encoded string
11615      * @scope Roo.Ajax
11616      * @param {String/HTMLElement} form
11617      * @return {String}
11618      */
11619     serializeForm : function(form){
11620         return Roo.lib.Ajax.serializeForm(form);
11621     }
11622 });/*
11623  * Based on:
11624  * Ext JS Library 1.1.1
11625  * Copyright(c) 2006-2007, Ext JS, LLC.
11626  *
11627  * Originally Released Under LGPL - original licence link has changed is not relivant.
11628  *
11629  * Fork - LGPL
11630  * <script type="text/javascript">
11631  */
11632
11633  
11634 /**
11635  * @class Roo.UpdateManager
11636  * @extends Roo.util.Observable
11637  * Provides AJAX-style update for Element object.<br><br>
11638  * Usage:<br>
11639  * <pre><code>
11640  * // Get it from a Roo.Element object
11641  * var el = Roo.get("foo");
11642  * var mgr = el.getUpdateManager();
11643  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11644  * ...
11645  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11646  * <br>
11647  * // or directly (returns the same UpdateManager instance)
11648  * var mgr = new Roo.UpdateManager("myElementId");
11649  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11650  * mgr.on("update", myFcnNeedsToKnow);
11651  * <br>
11652    // short handed call directly from the element object
11653    Roo.get("foo").load({
11654         url: "bar.php",
11655         scripts:true,
11656         params: "for=bar",
11657         text: "Loading Foo..."
11658    });
11659  * </code></pre>
11660  * @constructor
11661  * Create new UpdateManager directly.
11662  * @param {String/HTMLElement/Roo.Element} el The element to update
11663  * @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).
11664  */
11665 Roo.UpdateManager = function(el, forceNew){
11666     el = Roo.get(el);
11667     if(!forceNew && el.updateManager){
11668         return el.updateManager;
11669     }
11670     /**
11671      * The Element object
11672      * @type Roo.Element
11673      */
11674     this.el = el;
11675     /**
11676      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11677      * @type String
11678      */
11679     this.defaultUrl = null;
11680
11681     this.addEvents({
11682         /**
11683          * @event beforeupdate
11684          * Fired before an update is made, return false from your handler and the update is cancelled.
11685          * @param {Roo.Element} el
11686          * @param {String/Object/Function} url
11687          * @param {String/Object} params
11688          */
11689         "beforeupdate": true,
11690         /**
11691          * @event update
11692          * Fired after successful update is made.
11693          * @param {Roo.Element} el
11694          * @param {Object} oResponseObject The response Object
11695          */
11696         "update": true,
11697         /**
11698          * @event failure
11699          * Fired on update failure.
11700          * @param {Roo.Element} el
11701          * @param {Object} oResponseObject The response Object
11702          */
11703         "failure": true
11704     });
11705     var d = Roo.UpdateManager.defaults;
11706     /**
11707      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11708      * @type String
11709      */
11710     this.sslBlankUrl = d.sslBlankUrl;
11711     /**
11712      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11713      * @type Boolean
11714      */
11715     this.disableCaching = d.disableCaching;
11716     /**
11717      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11718      * @type String
11719      */
11720     this.indicatorText = d.indicatorText;
11721     /**
11722      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11723      * @type String
11724      */
11725     this.showLoadIndicator = d.showLoadIndicator;
11726     /**
11727      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11728      * @type Number
11729      */
11730     this.timeout = d.timeout;
11731
11732     /**
11733      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11734      * @type Boolean
11735      */
11736     this.loadScripts = d.loadScripts;
11737
11738     /**
11739      * Transaction object of current executing transaction
11740      */
11741     this.transaction = null;
11742
11743     /**
11744      * @private
11745      */
11746     this.autoRefreshProcId = null;
11747     /**
11748      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11749      * @type Function
11750      */
11751     this.refreshDelegate = this.refresh.createDelegate(this);
11752     /**
11753      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11754      * @type Function
11755      */
11756     this.updateDelegate = this.update.createDelegate(this);
11757     /**
11758      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11759      * @type Function
11760      */
11761     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11762     /**
11763      * @private
11764      */
11765     this.successDelegate = this.processSuccess.createDelegate(this);
11766     /**
11767      * @private
11768      */
11769     this.failureDelegate = this.processFailure.createDelegate(this);
11770
11771     if(!this.renderer){
11772      /**
11773       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11774       */
11775     this.renderer = new Roo.UpdateManager.BasicRenderer();
11776     }
11777     
11778     Roo.UpdateManager.superclass.constructor.call(this);
11779 };
11780
11781 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11782     /**
11783      * Get the Element this UpdateManager is bound to
11784      * @return {Roo.Element} The element
11785      */
11786     getEl : function(){
11787         return this.el;
11788     },
11789     /**
11790      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11791      * @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:
11792 <pre><code>
11793 um.update({<br/>
11794     url: "your-url.php",<br/>
11795     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11796     callback: yourFunction,<br/>
11797     scope: yourObject, //(optional scope)  <br/>
11798     discardUrl: false, <br/>
11799     nocache: false,<br/>
11800     text: "Loading...",<br/>
11801     timeout: 30,<br/>
11802     scripts: false<br/>
11803 });
11804 </code></pre>
11805      * The only required property is url. The optional properties nocache, text and scripts
11806      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11807      * @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}
11808      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11809      * @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.
11810      */
11811     update : function(url, params, callback, discardUrl){
11812         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11813             var method = this.method,
11814                 cfg;
11815             if(typeof url == "object"){ // must be config object
11816                 cfg = url;
11817                 url = cfg.url;
11818                 params = params || cfg.params;
11819                 callback = callback || cfg.callback;
11820                 discardUrl = discardUrl || cfg.discardUrl;
11821                 if(callback && cfg.scope){
11822                     callback = callback.createDelegate(cfg.scope);
11823                 }
11824                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11825                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11826                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11827                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11828                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11829             }
11830             this.showLoading();
11831             if(!discardUrl){
11832                 this.defaultUrl = url;
11833             }
11834             if(typeof url == "function"){
11835                 url = url.call(this);
11836             }
11837
11838             method = method || (params ? "POST" : "GET");
11839             if(method == "GET"){
11840                 url = this.prepareUrl(url);
11841             }
11842
11843             var o = Roo.apply(cfg ||{}, {
11844                 url : url,
11845                 params: params,
11846                 success: this.successDelegate,
11847                 failure: this.failureDelegate,
11848                 callback: undefined,
11849                 timeout: (this.timeout*1000),
11850                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11851             });
11852             Roo.log("updated manager called with timeout of " + o.timeout);
11853             this.transaction = Roo.Ajax.request(o);
11854         }
11855     },
11856
11857     /**
11858      * 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.
11859      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11860      * @param {String/HTMLElement} form The form Id or form element
11861      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11862      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11863      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11864      */
11865     formUpdate : function(form, url, reset, callback){
11866         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11867             if(typeof url == "function"){
11868                 url = url.call(this);
11869             }
11870             form = Roo.getDom(form);
11871             this.transaction = Roo.Ajax.request({
11872                 form: form,
11873                 url:url,
11874                 success: this.successDelegate,
11875                 failure: this.failureDelegate,
11876                 timeout: (this.timeout*1000),
11877                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11878             });
11879             this.showLoading.defer(1, this);
11880         }
11881     },
11882
11883     /**
11884      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11885      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11886      */
11887     refresh : function(callback){
11888         if(this.defaultUrl == null){
11889             return;
11890         }
11891         this.update(this.defaultUrl, null, callback, true);
11892     },
11893
11894     /**
11895      * Set this element to auto refresh.
11896      * @param {Number} interval How often to update (in seconds).
11897      * @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)
11898      * @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}
11899      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11900      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11901      */
11902     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11903         if(refreshNow){
11904             this.update(url || this.defaultUrl, params, callback, true);
11905         }
11906         if(this.autoRefreshProcId){
11907             clearInterval(this.autoRefreshProcId);
11908         }
11909         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11910     },
11911
11912     /**
11913      * Stop auto refresh on this element.
11914      */
11915      stopAutoRefresh : function(){
11916         if(this.autoRefreshProcId){
11917             clearInterval(this.autoRefreshProcId);
11918             delete this.autoRefreshProcId;
11919         }
11920     },
11921
11922     isAutoRefreshing : function(){
11923        return this.autoRefreshProcId ? true : false;
11924     },
11925     /**
11926      * Called to update the element to "Loading" state. Override to perform custom action.
11927      */
11928     showLoading : function(){
11929         if(this.showLoadIndicator){
11930             this.el.update(this.indicatorText);
11931         }
11932     },
11933
11934     /**
11935      * Adds unique parameter to query string if disableCaching = true
11936      * @private
11937      */
11938     prepareUrl : function(url){
11939         if(this.disableCaching){
11940             var append = "_dc=" + (new Date().getTime());
11941             if(url.indexOf("?") !== -1){
11942                 url += "&" + append;
11943             }else{
11944                 url += "?" + append;
11945             }
11946         }
11947         return url;
11948     },
11949
11950     /**
11951      * @private
11952      */
11953     processSuccess : function(response){
11954         this.transaction = null;
11955         if(response.argument.form && response.argument.reset){
11956             try{ // put in try/catch since some older FF releases had problems with this
11957                 response.argument.form.reset();
11958             }catch(e){}
11959         }
11960         if(this.loadScripts){
11961             this.renderer.render(this.el, response, this,
11962                 this.updateComplete.createDelegate(this, [response]));
11963         }else{
11964             this.renderer.render(this.el, response, this);
11965             this.updateComplete(response);
11966         }
11967     },
11968
11969     updateComplete : function(response){
11970         this.fireEvent("update", this.el, response);
11971         if(typeof response.argument.callback == "function"){
11972             response.argument.callback(this.el, true, response);
11973         }
11974     },
11975
11976     /**
11977      * @private
11978      */
11979     processFailure : function(response){
11980         this.transaction = null;
11981         this.fireEvent("failure", this.el, response);
11982         if(typeof response.argument.callback == "function"){
11983             response.argument.callback(this.el, false, response);
11984         }
11985     },
11986
11987     /**
11988      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11989      * @param {Object} renderer The object implementing the render() method
11990      */
11991     setRenderer : function(renderer){
11992         this.renderer = renderer;
11993     },
11994
11995     getRenderer : function(){
11996        return this.renderer;
11997     },
11998
11999     /**
12000      * Set the defaultUrl used for updates
12001      * @param {String/Function} defaultUrl The url or a function to call to get the url
12002      */
12003     setDefaultUrl : function(defaultUrl){
12004         this.defaultUrl = defaultUrl;
12005     },
12006
12007     /**
12008      * Aborts the executing transaction
12009      */
12010     abort : function(){
12011         if(this.transaction){
12012             Roo.Ajax.abort(this.transaction);
12013         }
12014     },
12015
12016     /**
12017      * Returns true if an update is in progress
12018      * @return {Boolean}
12019      */
12020     isUpdating : function(){
12021         if(this.transaction){
12022             return Roo.Ajax.isLoading(this.transaction);
12023         }
12024         return false;
12025     }
12026 });
12027
12028 /**
12029  * @class Roo.UpdateManager.defaults
12030  * @static (not really - but it helps the doc tool)
12031  * The defaults collection enables customizing the default properties of UpdateManager
12032  */
12033    Roo.UpdateManager.defaults = {
12034        /**
12035          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12036          * @type Number
12037          */
12038          timeout : 30,
12039
12040          /**
12041          * True to process scripts by default (Defaults to false).
12042          * @type Boolean
12043          */
12044         loadScripts : false,
12045
12046         /**
12047         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12048         * @type String
12049         */
12050         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12051         /**
12052          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12053          * @type Boolean
12054          */
12055         disableCaching : false,
12056         /**
12057          * Whether to show indicatorText when loading (Defaults to true).
12058          * @type Boolean
12059          */
12060         showLoadIndicator : true,
12061         /**
12062          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12063          * @type String
12064          */
12065         indicatorText : '<div class="loading-indicator">Loading...</div>'
12066    };
12067
12068 /**
12069  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12070  *Usage:
12071  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12072  * @param {String/HTMLElement/Roo.Element} el The element to update
12073  * @param {String} url The url
12074  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12075  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12076  * @static
12077  * @deprecated
12078  * @member Roo.UpdateManager
12079  */
12080 Roo.UpdateManager.updateElement = function(el, url, params, options){
12081     var um = Roo.get(el, true).getUpdateManager();
12082     Roo.apply(um, options);
12083     um.update(url, params, options ? options.callback : null);
12084 };
12085 // alias for backwards compat
12086 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12087 /**
12088  * @class Roo.UpdateManager.BasicRenderer
12089  * Default Content renderer. Updates the elements innerHTML with the responseText.
12090  */
12091 Roo.UpdateManager.BasicRenderer = function(){};
12092
12093 Roo.UpdateManager.BasicRenderer.prototype = {
12094     /**
12095      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12096      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12097      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12098      * @param {Roo.Element} el The element being rendered
12099      * @param {Object} response The YUI Connect response object
12100      * @param {UpdateManager} updateManager The calling update manager
12101      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12102      */
12103      render : function(el, response, updateManager, callback){
12104         el.update(response.responseText, updateManager.loadScripts, callback);
12105     }
12106 };
12107 /*
12108  * Based on:
12109  * Roo JS
12110  * (c)) Alan Knowles
12111  * Licence : LGPL
12112  */
12113
12114
12115 /**
12116  * @class Roo.DomTemplate
12117  * @extends Roo.Template
12118  * An effort at a dom based template engine..
12119  *
12120  * Similar to XTemplate, except it uses dom parsing to create the template..
12121  *
12122  * Supported features:
12123  *
12124  *  Tags:
12125
12126 <pre><code>
12127       {a_variable} - output encoded.
12128       {a_variable.format:("Y-m-d")} - call a method on the variable
12129       {a_variable:raw} - unencoded output
12130       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12131       {a_variable:this.method_on_template(...)} - call a method on the template object.
12132  
12133 </code></pre>
12134  *  The tpl tag:
12135 <pre><code>
12136         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12137         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12138         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12139         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12140   
12141 </code></pre>
12142  *      
12143  */
12144 Roo.DomTemplate = function()
12145 {
12146      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12147      if (this.html) {
12148         this.compile();
12149      }
12150 };
12151
12152
12153 Roo.extend(Roo.DomTemplate, Roo.Template, {
12154     /**
12155      * id counter for sub templates.
12156      */
12157     id : 0,
12158     /**
12159      * flag to indicate if dom parser is inside a pre,
12160      * it will strip whitespace if not.
12161      */
12162     inPre : false,
12163     
12164     /**
12165      * The various sub templates
12166      */
12167     tpls : false,
12168     
12169     
12170     
12171     /**
12172      *
12173      * basic tag replacing syntax
12174      * WORD:WORD()
12175      *
12176      * // you can fake an object call by doing this
12177      *  x.t:(test,tesT) 
12178      * 
12179      */
12180     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12181     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12182     
12183     iterChild : function (node, method) {
12184         
12185         var oldPre = this.inPre;
12186         if (node.tagName == 'PRE') {
12187             this.inPre = true;
12188         }
12189         for( var i = 0; i < node.childNodes.length; i++) {
12190             method.call(this, node.childNodes[i]);
12191         }
12192         this.inPre = oldPre;
12193     },
12194     
12195     
12196     
12197     /**
12198      * compile the template
12199      *
12200      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12201      *
12202      */
12203     compile: function()
12204     {
12205         var s = this.html;
12206         
12207         // covert the html into DOM...
12208         var doc = false;
12209         var div =false;
12210         try {
12211             doc = document.implementation.createHTMLDocument("");
12212             doc.documentElement.innerHTML =   this.html  ;
12213             div = doc.documentElement;
12214         } catch (e) {
12215             // old IE... - nasty -- it causes all sorts of issues.. with
12216             // images getting pulled from server..
12217             div = document.createElement('div');
12218             div.innerHTML = this.html;
12219         }
12220         //doc.documentElement.innerHTML = htmlBody
12221          
12222         
12223         
12224         this.tpls = [];
12225         var _t = this;
12226         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12227         
12228         var tpls = this.tpls;
12229         
12230         // create a top level template from the snippet..
12231         
12232         //Roo.log(div.innerHTML);
12233         
12234         var tpl = {
12235             uid : 'master',
12236             id : this.id++,
12237             attr : false,
12238             value : false,
12239             body : div.innerHTML,
12240             
12241             forCall : false,
12242             execCall : false,
12243             dom : div,
12244             isTop : true
12245             
12246         };
12247         tpls.unshift(tpl);
12248         
12249         
12250         // compile them...
12251         this.tpls = [];
12252         Roo.each(tpls, function(tp){
12253             this.compileTpl(tp);
12254             this.tpls[tp.id] = tp;
12255         }, this);
12256         
12257         this.master = tpls[0];
12258         return this;
12259         
12260         
12261     },
12262     
12263     compileNode : function(node, istop) {
12264         // test for
12265         //Roo.log(node);
12266         
12267         
12268         // skip anything not a tag..
12269         if (node.nodeType != 1) {
12270             if (node.nodeType == 3 && !this.inPre) {
12271                 // reduce white space..
12272                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12273                 
12274             }
12275             return;
12276         }
12277         
12278         var tpl = {
12279             uid : false,
12280             id : false,
12281             attr : false,
12282             value : false,
12283             body : '',
12284             
12285             forCall : false,
12286             execCall : false,
12287             dom : false,
12288             isTop : istop
12289             
12290             
12291         };
12292         
12293         
12294         switch(true) {
12295             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12296             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12297             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12298             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12299             // no default..
12300         }
12301         
12302         
12303         if (!tpl.attr) {
12304             // just itterate children..
12305             this.iterChild(node,this.compileNode);
12306             return;
12307         }
12308         tpl.uid = this.id++;
12309         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12310         node.removeAttribute('roo-'+ tpl.attr);
12311         if (tpl.attr != 'name') {
12312             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12313             node.parentNode.replaceChild(placeholder,  node);
12314         } else {
12315             
12316             var placeholder =  document.createElement('span');
12317             placeholder.className = 'roo-tpl-' + tpl.value;
12318             node.parentNode.replaceChild(placeholder,  node);
12319         }
12320         
12321         // parent now sees '{domtplXXXX}
12322         this.iterChild(node,this.compileNode);
12323         
12324         // we should now have node body...
12325         var div = document.createElement('div');
12326         div.appendChild(node);
12327         tpl.dom = node;
12328         // this has the unfortunate side effect of converting tagged attributes
12329         // eg. href="{...}" into %7C...%7D
12330         // this has been fixed by searching for those combo's although it's a bit hacky..
12331         
12332         
12333         tpl.body = div.innerHTML;
12334         
12335         
12336          
12337         tpl.id = tpl.uid;
12338         switch(tpl.attr) {
12339             case 'for' :
12340                 switch (tpl.value) {
12341                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12342                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12343                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12344                 }
12345                 break;
12346             
12347             case 'exec':
12348                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12349                 break;
12350             
12351             case 'if':     
12352                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12353                 break;
12354             
12355             case 'name':
12356                 tpl.id  = tpl.value; // replace non characters???
12357                 break;
12358             
12359         }
12360         
12361         
12362         this.tpls.push(tpl);
12363         
12364         
12365         
12366     },
12367     
12368     
12369     
12370     
12371     /**
12372      * Compile a segment of the template into a 'sub-template'
12373      *
12374      * 
12375      * 
12376      *
12377      */
12378     compileTpl : function(tpl)
12379     {
12380         var fm = Roo.util.Format;
12381         var useF = this.disableFormats !== true;
12382         
12383         var sep = Roo.isGecko ? "+\n" : ",\n";
12384         
12385         var undef = function(str) {
12386             Roo.debug && Roo.log("Property not found :"  + str);
12387             return '';
12388         };
12389           
12390         //Roo.log(tpl.body);
12391         
12392         
12393         
12394         var fn = function(m, lbrace, name, format, args)
12395         {
12396             //Roo.log("ARGS");
12397             //Roo.log(arguments);
12398             args = args ? args.replace(/\\'/g,"'") : args;
12399             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12400             if (typeof(format) == 'undefined') {
12401                 format =  'htmlEncode'; 
12402             }
12403             if (format == 'raw' ) {
12404                 format = false;
12405             }
12406             
12407             if(name.substr(0, 6) == 'domtpl'){
12408                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12409             }
12410             
12411             // build an array of options to determine if value is undefined..
12412             
12413             // basically get 'xxxx.yyyy' then do
12414             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12415             //    (function () { Roo.log("Property not found"); return ''; })() :
12416             //    ......
12417             
12418             var udef_ar = [];
12419             var lookfor = '';
12420             Roo.each(name.split('.'), function(st) {
12421                 lookfor += (lookfor.length ? '.': '') + st;
12422                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12423             });
12424             
12425             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12426             
12427             
12428             if(format && useF){
12429                 
12430                 args = args ? ',' + args : "";
12431                  
12432                 if(format.substr(0, 5) != "this."){
12433                     format = "fm." + format + '(';
12434                 }else{
12435                     format = 'this.call("'+ format.substr(5) + '", ';
12436                     args = ", values";
12437                 }
12438                 
12439                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12440             }
12441              
12442             if (args && args.length) {
12443                 // called with xxyx.yuu:(test,test)
12444                 // change to ()
12445                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12446             }
12447             // raw.. - :raw modifier..
12448             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12449             
12450         };
12451         var body;
12452         // branched to use + in gecko and [].join() in others
12453         if(Roo.isGecko){
12454             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12455                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12456                     "';};};";
12457         }else{
12458             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12459             body.push(tpl.body.replace(/(\r\n|\n)/g,
12460                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12461             body.push("'].join('');};};");
12462             body = body.join('');
12463         }
12464         
12465         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12466        
12467         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12468         eval(body);
12469         
12470         return this;
12471     },
12472      
12473     /**
12474      * same as applyTemplate, except it's done to one of the subTemplates
12475      * when using named templates, you can do:
12476      *
12477      * var str = pl.applySubTemplate('your-name', values);
12478      *
12479      * 
12480      * @param {Number} id of the template
12481      * @param {Object} values to apply to template
12482      * @param {Object} parent (normaly the instance of this object)
12483      */
12484     applySubTemplate : function(id, values, parent)
12485     {
12486         
12487         
12488         var t = this.tpls[id];
12489         
12490         
12491         try { 
12492             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12493                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12494                 return '';
12495             }
12496         } catch(e) {
12497             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12498             Roo.log(values);
12499           
12500             return '';
12501         }
12502         try { 
12503             
12504             if(t.execCall && t.execCall.call(this, values, parent)){
12505                 return '';
12506             }
12507         } catch(e) {
12508             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12509             Roo.log(values);
12510             return '';
12511         }
12512         
12513         try {
12514             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12515             parent = t.target ? values : parent;
12516             if(t.forCall && vs instanceof Array){
12517                 var buf = [];
12518                 for(var i = 0, len = vs.length; i < len; i++){
12519                     try {
12520                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12521                     } catch (e) {
12522                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12523                         Roo.log(e.body);
12524                         //Roo.log(t.compiled);
12525                         Roo.log(vs[i]);
12526                     }   
12527                 }
12528                 return buf.join('');
12529             }
12530         } catch (e) {
12531             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12532             Roo.log(values);
12533             return '';
12534         }
12535         try {
12536             return t.compiled.call(this, vs, parent);
12537         } catch (e) {
12538             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12539             Roo.log(e.body);
12540             //Roo.log(t.compiled);
12541             Roo.log(values);
12542             return '';
12543         }
12544     },
12545
12546    
12547
12548     applyTemplate : function(values){
12549         return this.master.compiled.call(this, values, {});
12550         //var s = this.subs;
12551     },
12552
12553     apply : function(){
12554         return this.applyTemplate.apply(this, arguments);
12555     }
12556
12557  });
12558
12559 Roo.DomTemplate.from = function(el){
12560     el = Roo.getDom(el);
12561     return new Roo.Domtemplate(el.value || el.innerHTML);
12562 };/*
12563  * Based on:
12564  * Ext JS Library 1.1.1
12565  * Copyright(c) 2006-2007, Ext JS, LLC.
12566  *
12567  * Originally Released Under LGPL - original licence link has changed is not relivant.
12568  *
12569  * Fork - LGPL
12570  * <script type="text/javascript">
12571  */
12572
12573 /**
12574  * @class Roo.util.DelayedTask
12575  * Provides a convenient method of performing setTimeout where a new
12576  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12577  * You can use this class to buffer
12578  * the keypress events for a certain number of milliseconds, and perform only if they stop
12579  * for that amount of time.
12580  * @constructor The parameters to this constructor serve as defaults and are not required.
12581  * @param {Function} fn (optional) The default function to timeout
12582  * @param {Object} scope (optional) The default scope of that timeout
12583  * @param {Array} args (optional) The default Array of arguments
12584  */
12585 Roo.util.DelayedTask = function(fn, scope, args){
12586     var id = null, d, t;
12587
12588     var call = function(){
12589         var now = new Date().getTime();
12590         if(now - t >= d){
12591             clearInterval(id);
12592             id = null;
12593             fn.apply(scope, args || []);
12594         }
12595     };
12596     /**
12597      * Cancels any pending timeout and queues a new one
12598      * @param {Number} delay The milliseconds to delay
12599      * @param {Function} newFn (optional) Overrides function passed to constructor
12600      * @param {Object} newScope (optional) Overrides scope passed to constructor
12601      * @param {Array} newArgs (optional) Overrides args passed to constructor
12602      */
12603     this.delay = function(delay, newFn, newScope, newArgs){
12604         if(id && delay != d){
12605             this.cancel();
12606         }
12607         d = delay;
12608         t = new Date().getTime();
12609         fn = newFn || fn;
12610         scope = newScope || scope;
12611         args = newArgs || args;
12612         if(!id){
12613             id = setInterval(call, d);
12614         }
12615     };
12616
12617     /**
12618      * Cancel the last queued timeout
12619      */
12620     this.cancel = function(){
12621         if(id){
12622             clearInterval(id);
12623             id = null;
12624         }
12625     };
12626 };/*
12627  * Based on:
12628  * Ext JS Library 1.1.1
12629  * Copyright(c) 2006-2007, Ext JS, LLC.
12630  *
12631  * Originally Released Under LGPL - original licence link has changed is not relivant.
12632  *
12633  * Fork - LGPL
12634  * <script type="text/javascript">
12635  */
12636  
12637  
12638 Roo.util.TaskRunner = function(interval){
12639     interval = interval || 10;
12640     var tasks = [], removeQueue = [];
12641     var id = 0;
12642     var running = false;
12643
12644     var stopThread = function(){
12645         running = false;
12646         clearInterval(id);
12647         id = 0;
12648     };
12649
12650     var startThread = function(){
12651         if(!running){
12652             running = true;
12653             id = setInterval(runTasks, interval);
12654         }
12655     };
12656
12657     var removeTask = function(task){
12658         removeQueue.push(task);
12659         if(task.onStop){
12660             task.onStop();
12661         }
12662     };
12663
12664     var runTasks = function(){
12665         if(removeQueue.length > 0){
12666             for(var i = 0, len = removeQueue.length; i < len; i++){
12667                 tasks.remove(removeQueue[i]);
12668             }
12669             removeQueue = [];
12670             if(tasks.length < 1){
12671                 stopThread();
12672                 return;
12673             }
12674         }
12675         var now = new Date().getTime();
12676         for(var i = 0, len = tasks.length; i < len; ++i){
12677             var t = tasks[i];
12678             var itime = now - t.taskRunTime;
12679             if(t.interval <= itime){
12680                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12681                 t.taskRunTime = now;
12682                 if(rt === false || t.taskRunCount === t.repeat){
12683                     removeTask(t);
12684                     return;
12685                 }
12686             }
12687             if(t.duration && t.duration <= (now - t.taskStartTime)){
12688                 removeTask(t);
12689             }
12690         }
12691     };
12692
12693     /**
12694      * Queues a new task.
12695      * @param {Object} task
12696      */
12697     this.start = function(task){
12698         tasks.push(task);
12699         task.taskStartTime = new Date().getTime();
12700         task.taskRunTime = 0;
12701         task.taskRunCount = 0;
12702         startThread();
12703         return task;
12704     };
12705
12706     this.stop = function(task){
12707         removeTask(task);
12708         return task;
12709     };
12710
12711     this.stopAll = function(){
12712         stopThread();
12713         for(var i = 0, len = tasks.length; i < len; i++){
12714             if(tasks[i].onStop){
12715                 tasks[i].onStop();
12716             }
12717         }
12718         tasks = [];
12719         removeQueue = [];
12720     };
12721 };
12722
12723 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12724  * Based on:
12725  * Ext JS Library 1.1.1
12726  * Copyright(c) 2006-2007, Ext JS, LLC.
12727  *
12728  * Originally Released Under LGPL - original licence link has changed is not relivant.
12729  *
12730  * Fork - LGPL
12731  * <script type="text/javascript">
12732  */
12733
12734  
12735 /**
12736  * @class Roo.util.MixedCollection
12737  * @extends Roo.util.Observable
12738  * A Collection class that maintains both numeric indexes and keys and exposes events.
12739  * @constructor
12740  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12741  * collection (defaults to false)
12742  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12743  * and return the key value for that item.  This is used when available to look up the key on items that
12744  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12745  * equivalent to providing an implementation for the {@link #getKey} method.
12746  */
12747 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12748     this.items = [];
12749     this.map = {};
12750     this.keys = [];
12751     this.length = 0;
12752     this.addEvents({
12753         /**
12754          * @event clear
12755          * Fires when the collection is cleared.
12756          */
12757         "clear" : true,
12758         /**
12759          * @event add
12760          * Fires when an item is added to the collection.
12761          * @param {Number} index The index at which the item was added.
12762          * @param {Object} o The item added.
12763          * @param {String} key The key associated with the added item.
12764          */
12765         "add" : true,
12766         /**
12767          * @event replace
12768          * Fires when an item is replaced in the collection.
12769          * @param {String} key he key associated with the new added.
12770          * @param {Object} old The item being replaced.
12771          * @param {Object} new The new item.
12772          */
12773         "replace" : true,
12774         /**
12775          * @event remove
12776          * Fires when an item is removed from the collection.
12777          * @param {Object} o The item being removed.
12778          * @param {String} key (optional) The key associated with the removed item.
12779          */
12780         "remove" : true,
12781         "sort" : true
12782     });
12783     this.allowFunctions = allowFunctions === true;
12784     if(keyFn){
12785         this.getKey = keyFn;
12786     }
12787     Roo.util.MixedCollection.superclass.constructor.call(this);
12788 };
12789
12790 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12791     allowFunctions : false,
12792     
12793 /**
12794  * Adds an item to the collection.
12795  * @param {String} key The key to associate with the item
12796  * @param {Object} o The item to add.
12797  * @return {Object} The item added.
12798  */
12799     add : function(key, o){
12800         if(arguments.length == 1){
12801             o = arguments[0];
12802             key = this.getKey(o);
12803         }
12804         if(typeof key == "undefined" || key === null){
12805             this.length++;
12806             this.items.push(o);
12807             this.keys.push(null);
12808         }else{
12809             var old = this.map[key];
12810             if(old){
12811                 return this.replace(key, o);
12812             }
12813             this.length++;
12814             this.items.push(o);
12815             this.map[key] = o;
12816             this.keys.push(key);
12817         }
12818         this.fireEvent("add", this.length-1, o, key);
12819         return o;
12820     },
12821        
12822 /**
12823   * MixedCollection has a generic way to fetch keys if you implement getKey.
12824 <pre><code>
12825 // normal way
12826 var mc = new Roo.util.MixedCollection();
12827 mc.add(someEl.dom.id, someEl);
12828 mc.add(otherEl.dom.id, otherEl);
12829 //and so on
12830
12831 // using getKey
12832 var mc = new Roo.util.MixedCollection();
12833 mc.getKey = function(el){
12834    return el.dom.id;
12835 };
12836 mc.add(someEl);
12837 mc.add(otherEl);
12838
12839 // or via the constructor
12840 var mc = new Roo.util.MixedCollection(false, function(el){
12841    return el.dom.id;
12842 });
12843 mc.add(someEl);
12844 mc.add(otherEl);
12845 </code></pre>
12846  * @param o {Object} The item for which to find the key.
12847  * @return {Object} The key for the passed item.
12848  */
12849     getKey : function(o){
12850          return o.id; 
12851     },
12852    
12853 /**
12854  * Replaces an item in the collection.
12855  * @param {String} key The key associated with the item to replace, or the item to replace.
12856  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12857  * @return {Object}  The new item.
12858  */
12859     replace : function(key, o){
12860         if(arguments.length == 1){
12861             o = arguments[0];
12862             key = this.getKey(o);
12863         }
12864         var old = this.item(key);
12865         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12866              return this.add(key, o);
12867         }
12868         var index = this.indexOfKey(key);
12869         this.items[index] = o;
12870         this.map[key] = o;
12871         this.fireEvent("replace", key, old, o);
12872         return o;
12873     },
12874    
12875 /**
12876  * Adds all elements of an Array or an Object to the collection.
12877  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12878  * an Array of values, each of which are added to the collection.
12879  */
12880     addAll : function(objs){
12881         if(arguments.length > 1 || objs instanceof Array){
12882             var args = arguments.length > 1 ? arguments : objs;
12883             for(var i = 0, len = args.length; i < len; i++){
12884                 this.add(args[i]);
12885             }
12886         }else{
12887             for(var key in objs){
12888                 if(this.allowFunctions || typeof objs[key] != "function"){
12889                     this.add(key, objs[key]);
12890                 }
12891             }
12892         }
12893     },
12894    
12895 /**
12896  * Executes the specified function once for every item in the collection, passing each
12897  * item as the first and only parameter. returning false from the function will stop the iteration.
12898  * @param {Function} fn The function to execute for each item.
12899  * @param {Object} scope (optional) The scope in which to execute the function.
12900  */
12901     each : function(fn, scope){
12902         var items = [].concat(this.items); // each safe for removal
12903         for(var i = 0, len = items.length; i < len; i++){
12904             if(fn.call(scope || items[i], items[i], i, len) === false){
12905                 break;
12906             }
12907         }
12908     },
12909    
12910 /**
12911  * Executes the specified function once for every key in the collection, passing each
12912  * key, and its associated item as the first two parameters.
12913  * @param {Function} fn The function to execute for each item.
12914  * @param {Object} scope (optional) The scope in which to execute the function.
12915  */
12916     eachKey : function(fn, scope){
12917         for(var i = 0, len = this.keys.length; i < len; i++){
12918             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12919         }
12920     },
12921    
12922 /**
12923  * Returns the first item in the collection which elicits a true return value from the
12924  * passed selection function.
12925  * @param {Function} fn The selection function to execute for each item.
12926  * @param {Object} scope (optional) The scope in which to execute the function.
12927  * @return {Object} The first item in the collection which returned true from the selection function.
12928  */
12929     find : function(fn, scope){
12930         for(var i = 0, len = this.items.length; i < len; i++){
12931             if(fn.call(scope || window, this.items[i], this.keys[i])){
12932                 return this.items[i];
12933             }
12934         }
12935         return null;
12936     },
12937    
12938 /**
12939  * Inserts an item at the specified index in the collection.
12940  * @param {Number} index The index to insert the item at.
12941  * @param {String} key The key to associate with the new item, or the item itself.
12942  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12943  * @return {Object} The item inserted.
12944  */
12945     insert : function(index, key, o){
12946         if(arguments.length == 2){
12947             o = arguments[1];
12948             key = this.getKey(o);
12949         }
12950         if(index >= this.length){
12951             return this.add(key, o);
12952         }
12953         this.length++;
12954         this.items.splice(index, 0, o);
12955         if(typeof key != "undefined" && key != null){
12956             this.map[key] = o;
12957         }
12958         this.keys.splice(index, 0, key);
12959         this.fireEvent("add", index, o, key);
12960         return o;
12961     },
12962    
12963 /**
12964  * Removed an item from the collection.
12965  * @param {Object} o The item to remove.
12966  * @return {Object} The item removed.
12967  */
12968     remove : function(o){
12969         return this.removeAt(this.indexOf(o));
12970     },
12971    
12972 /**
12973  * Remove an item from a specified index in the collection.
12974  * @param {Number} index The index within the collection of the item to remove.
12975  */
12976     removeAt : function(index){
12977         if(index < this.length && index >= 0){
12978             this.length--;
12979             var o = this.items[index];
12980             this.items.splice(index, 1);
12981             var key = this.keys[index];
12982             if(typeof key != "undefined"){
12983                 delete this.map[key];
12984             }
12985             this.keys.splice(index, 1);
12986             this.fireEvent("remove", o, key);
12987         }
12988     },
12989    
12990 /**
12991  * Removed an item associated with the passed key fom the collection.
12992  * @param {String} key The key of the item to remove.
12993  */
12994     removeKey : function(key){
12995         return this.removeAt(this.indexOfKey(key));
12996     },
12997    
12998 /**
12999  * Returns the number of items in the collection.
13000  * @return {Number} the number of items in the collection.
13001  */
13002     getCount : function(){
13003         return this.length; 
13004     },
13005    
13006 /**
13007  * Returns index within the collection of the passed Object.
13008  * @param {Object} o The item to find the index of.
13009  * @return {Number} index of the item.
13010  */
13011     indexOf : function(o){
13012         if(!this.items.indexOf){
13013             for(var i = 0, len = this.items.length; i < len; i++){
13014                 if(this.items[i] == o) return i;
13015             }
13016             return -1;
13017         }else{
13018             return this.items.indexOf(o);
13019         }
13020     },
13021    
13022 /**
13023  * Returns index within the collection of the passed key.
13024  * @param {String} key The key to find the index of.
13025  * @return {Number} index of the key.
13026  */
13027     indexOfKey : function(key){
13028         if(!this.keys.indexOf){
13029             for(var i = 0, len = this.keys.length; i < len; i++){
13030                 if(this.keys[i] == key) return i;
13031             }
13032             return -1;
13033         }else{
13034             return this.keys.indexOf(key);
13035         }
13036     },
13037    
13038 /**
13039  * Returns the item associated with the passed key OR index. Key has priority over index.
13040  * @param {String/Number} key The key or index of the item.
13041  * @return {Object} The item associated with the passed key.
13042  */
13043     item : function(key){
13044         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13045         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13046     },
13047     
13048 /**
13049  * Returns the item at the specified index.
13050  * @param {Number} index The index of the item.
13051  * @return {Object}
13052  */
13053     itemAt : function(index){
13054         return this.items[index];
13055     },
13056     
13057 /**
13058  * Returns the item associated with the passed key.
13059  * @param {String/Number} key The key of the item.
13060  * @return {Object} The item associated with the passed key.
13061  */
13062     key : function(key){
13063         return this.map[key];
13064     },
13065    
13066 /**
13067  * Returns true if the collection contains the passed Object as an item.
13068  * @param {Object} o  The Object to look for in the collection.
13069  * @return {Boolean} True if the collection contains the Object as an item.
13070  */
13071     contains : function(o){
13072         return this.indexOf(o) != -1;
13073     },
13074    
13075 /**
13076  * Returns true if the collection contains the passed Object as a key.
13077  * @param {String} key The key to look for in the collection.
13078  * @return {Boolean} True if the collection contains the Object as a key.
13079  */
13080     containsKey : function(key){
13081         return typeof this.map[key] != "undefined";
13082     },
13083    
13084 /**
13085  * Removes all items from the collection.
13086  */
13087     clear : function(){
13088         this.length = 0;
13089         this.items = [];
13090         this.keys = [];
13091         this.map = {};
13092         this.fireEvent("clear");
13093     },
13094    
13095 /**
13096  * Returns the first item in the collection.
13097  * @return {Object} the first item in the collection..
13098  */
13099     first : function(){
13100         return this.items[0]; 
13101     },
13102    
13103 /**
13104  * Returns the last item in the collection.
13105  * @return {Object} the last item in the collection..
13106  */
13107     last : function(){
13108         return this.items[this.length-1];   
13109     },
13110     
13111     _sort : function(property, dir, fn){
13112         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13113         fn = fn || function(a, b){
13114             return a-b;
13115         };
13116         var c = [], k = this.keys, items = this.items;
13117         for(var i = 0, len = items.length; i < len; i++){
13118             c[c.length] = {key: k[i], value: items[i], index: i};
13119         }
13120         c.sort(function(a, b){
13121             var v = fn(a[property], b[property]) * dsc;
13122             if(v == 0){
13123                 v = (a.index < b.index ? -1 : 1);
13124             }
13125             return v;
13126         });
13127         for(var i = 0, len = c.length; i < len; i++){
13128             items[i] = c[i].value;
13129             k[i] = c[i].key;
13130         }
13131         this.fireEvent("sort", this);
13132     },
13133     
13134     /**
13135      * Sorts this collection with the passed comparison function
13136      * @param {String} direction (optional) "ASC" or "DESC"
13137      * @param {Function} fn (optional) comparison function
13138      */
13139     sort : function(dir, fn){
13140         this._sort("value", dir, fn);
13141     },
13142     
13143     /**
13144      * Sorts this collection by keys
13145      * @param {String} direction (optional) "ASC" or "DESC"
13146      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13147      */
13148     keySort : function(dir, fn){
13149         this._sort("key", dir, fn || function(a, b){
13150             return String(a).toUpperCase()-String(b).toUpperCase();
13151         });
13152     },
13153     
13154     /**
13155      * Returns a range of items in this collection
13156      * @param {Number} startIndex (optional) defaults to 0
13157      * @param {Number} endIndex (optional) default to the last item
13158      * @return {Array} An array of items
13159      */
13160     getRange : function(start, end){
13161         var items = this.items;
13162         if(items.length < 1){
13163             return [];
13164         }
13165         start = start || 0;
13166         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13167         var r = [];
13168         if(start <= end){
13169             for(var i = start; i <= end; i++) {
13170                     r[r.length] = items[i];
13171             }
13172         }else{
13173             for(var i = start; i >= end; i--) {
13174                     r[r.length] = items[i];
13175             }
13176         }
13177         return r;
13178     },
13179         
13180     /**
13181      * Filter the <i>objects</i> in this collection by a specific property. 
13182      * Returns a new collection that has been filtered.
13183      * @param {String} property A property on your objects
13184      * @param {String/RegExp} value Either string that the property values 
13185      * should start with or a RegExp to test against the property
13186      * @return {MixedCollection} The new filtered collection
13187      */
13188     filter : function(property, value){
13189         if(!value.exec){ // not a regex
13190             value = String(value);
13191             if(value.length == 0){
13192                 return this.clone();
13193             }
13194             value = new RegExp("^" + Roo.escapeRe(value), "i");
13195         }
13196         return this.filterBy(function(o){
13197             return o && value.test(o[property]);
13198         });
13199         },
13200     
13201     /**
13202      * Filter by a function. * Returns a new collection that has been filtered.
13203      * The passed function will be called with each 
13204      * object in the collection. If the function returns true, the value is included 
13205      * otherwise it is filtered.
13206      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13207      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13208      * @return {MixedCollection} The new filtered collection
13209      */
13210     filterBy : function(fn, scope){
13211         var r = new Roo.util.MixedCollection();
13212         r.getKey = this.getKey;
13213         var k = this.keys, it = this.items;
13214         for(var i = 0, len = it.length; i < len; i++){
13215             if(fn.call(scope||this, it[i], k[i])){
13216                                 r.add(k[i], it[i]);
13217                         }
13218         }
13219         return r;
13220     },
13221     
13222     /**
13223      * Creates a duplicate of this collection
13224      * @return {MixedCollection}
13225      */
13226     clone : function(){
13227         var r = new Roo.util.MixedCollection();
13228         var k = this.keys, it = this.items;
13229         for(var i = 0, len = it.length; i < len; i++){
13230             r.add(k[i], it[i]);
13231         }
13232         r.getKey = this.getKey;
13233         return r;
13234     }
13235 });
13236 /**
13237  * Returns the item associated with the passed key or index.
13238  * @method
13239  * @param {String/Number} key The key or index of the item.
13240  * @return {Object} The item associated with the passed key.
13241  */
13242 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13243  * Based on:
13244  * Ext JS Library 1.1.1
13245  * Copyright(c) 2006-2007, Ext JS, LLC.
13246  *
13247  * Originally Released Under LGPL - original licence link has changed is not relivant.
13248  *
13249  * Fork - LGPL
13250  * <script type="text/javascript">
13251  */
13252 /**
13253  * @class Roo.util.JSON
13254  * Modified version of Douglas Crockford"s json.js that doesn"t
13255  * mess with the Object prototype 
13256  * http://www.json.org/js.html
13257  * @singleton
13258  */
13259 Roo.util.JSON = new (function(){
13260     var useHasOwn = {}.hasOwnProperty ? true : false;
13261     
13262     // crashes Safari in some instances
13263     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13264     
13265     var pad = function(n) {
13266         return n < 10 ? "0" + n : n;
13267     };
13268     
13269     var m = {
13270         "\b": '\\b',
13271         "\t": '\\t',
13272         "\n": '\\n',
13273         "\f": '\\f',
13274         "\r": '\\r',
13275         '"' : '\\"',
13276         "\\": '\\\\'
13277     };
13278
13279     var encodeString = function(s){
13280         if (/["\\\x00-\x1f]/.test(s)) {
13281             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13282                 var c = m[b];
13283                 if(c){
13284                     return c;
13285                 }
13286                 c = b.charCodeAt();
13287                 return "\\u00" +
13288                     Math.floor(c / 16).toString(16) +
13289                     (c % 16).toString(16);
13290             }) + '"';
13291         }
13292         return '"' + s + '"';
13293     };
13294     
13295     var encodeArray = function(o){
13296         var a = ["["], b, i, l = o.length, v;
13297             for (i = 0; i < l; i += 1) {
13298                 v = o[i];
13299                 switch (typeof v) {
13300                     case "undefined":
13301                     case "function":
13302                     case "unknown":
13303                         break;
13304                     default:
13305                         if (b) {
13306                             a.push(',');
13307                         }
13308                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13309                         b = true;
13310                 }
13311             }
13312             a.push("]");
13313             return a.join("");
13314     };
13315     
13316     var encodeDate = function(o){
13317         return '"' + o.getFullYear() + "-" +
13318                 pad(o.getMonth() + 1) + "-" +
13319                 pad(o.getDate()) + "T" +
13320                 pad(o.getHours()) + ":" +
13321                 pad(o.getMinutes()) + ":" +
13322                 pad(o.getSeconds()) + '"';
13323     };
13324     
13325     /**
13326      * Encodes an Object, Array or other value
13327      * @param {Mixed} o The variable to encode
13328      * @return {String} The JSON string
13329      */
13330     this.encode = function(o)
13331     {
13332         // should this be extended to fully wrap stringify..
13333         
13334         if(typeof o == "undefined" || o === null){
13335             return "null";
13336         }else if(o instanceof Array){
13337             return encodeArray(o);
13338         }else if(o instanceof Date){
13339             return encodeDate(o);
13340         }else if(typeof o == "string"){
13341             return encodeString(o);
13342         }else if(typeof o == "number"){
13343             return isFinite(o) ? String(o) : "null";
13344         }else if(typeof o == "boolean"){
13345             return String(o);
13346         }else {
13347             var a = ["{"], b, i, v;
13348             for (i in o) {
13349                 if(!useHasOwn || o.hasOwnProperty(i)) {
13350                     v = o[i];
13351                     switch (typeof v) {
13352                     case "undefined":
13353                     case "function":
13354                     case "unknown":
13355                         break;
13356                     default:
13357                         if(b){
13358                             a.push(',');
13359                         }
13360                         a.push(this.encode(i), ":",
13361                                 v === null ? "null" : this.encode(v));
13362                         b = true;
13363                     }
13364                 }
13365             }
13366             a.push("}");
13367             return a.join("");
13368         }
13369     };
13370     
13371     /**
13372      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13373      * @param {String} json The JSON string
13374      * @return {Object} The resulting object
13375      */
13376     this.decode = function(json){
13377         
13378         return  /** eval:var:json */ eval("(" + json + ')');
13379     };
13380 })();
13381 /** 
13382  * Shorthand for {@link Roo.util.JSON#encode}
13383  * @member Roo encode 
13384  * @method */
13385 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13386 /** 
13387  * Shorthand for {@link Roo.util.JSON#decode}
13388  * @member Roo decode 
13389  * @method */
13390 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13391 /*
13392  * Based on:
13393  * Ext JS Library 1.1.1
13394  * Copyright(c) 2006-2007, Ext JS, LLC.
13395  *
13396  * Originally Released Under LGPL - original licence link has changed is not relivant.
13397  *
13398  * Fork - LGPL
13399  * <script type="text/javascript">
13400  */
13401  
13402 /**
13403  * @class Roo.util.Format
13404  * Reusable data formatting functions
13405  * @singleton
13406  */
13407 Roo.util.Format = function(){
13408     var trimRe = /^\s+|\s+$/g;
13409     return {
13410         /**
13411          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13412          * @param {String} value The string to truncate
13413          * @param {Number} length The maximum length to allow before truncating
13414          * @return {String} The converted text
13415          */
13416         ellipsis : function(value, len){
13417             if(value && value.length > len){
13418                 return value.substr(0, len-3)+"...";
13419             }
13420             return value;
13421         },
13422
13423         /**
13424          * Checks a reference and converts it to empty string if it is undefined
13425          * @param {Mixed} value Reference to check
13426          * @return {Mixed} Empty string if converted, otherwise the original value
13427          */
13428         undef : function(value){
13429             return typeof value != "undefined" ? value : "";
13430         },
13431
13432         /**
13433          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13434          * @param {String} value The string to encode
13435          * @return {String} The encoded text
13436          */
13437         htmlEncode : function(value){
13438             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13439         },
13440
13441         /**
13442          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13443          * @param {String} value The string to decode
13444          * @return {String} The decoded text
13445          */
13446         htmlDecode : function(value){
13447             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13448         },
13449
13450         /**
13451          * Trims any whitespace from either side of a string
13452          * @param {String} value The text to trim
13453          * @return {String} The trimmed text
13454          */
13455         trim : function(value){
13456             return String(value).replace(trimRe, "");
13457         },
13458
13459         /**
13460          * Returns a substring from within an original string
13461          * @param {String} value The original text
13462          * @param {Number} start The start index of the substring
13463          * @param {Number} length The length of the substring
13464          * @return {String} The substring
13465          */
13466         substr : function(value, start, length){
13467             return String(value).substr(start, length);
13468         },
13469
13470         /**
13471          * Converts a string to all lower case letters
13472          * @param {String} value The text to convert
13473          * @return {String} The converted text
13474          */
13475         lowercase : function(value){
13476             return String(value).toLowerCase();
13477         },
13478
13479         /**
13480          * Converts a string to all upper case letters
13481          * @param {String} value The text to convert
13482          * @return {String} The converted text
13483          */
13484         uppercase : function(value){
13485             return String(value).toUpperCase();
13486         },
13487
13488         /**
13489          * Converts the first character only of a string to upper case
13490          * @param {String} value The text to convert
13491          * @return {String} The converted text
13492          */
13493         capitalize : function(value){
13494             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13495         },
13496
13497         // private
13498         call : function(value, fn){
13499             if(arguments.length > 2){
13500                 var args = Array.prototype.slice.call(arguments, 2);
13501                 args.unshift(value);
13502                  
13503                 return /** eval:var:value */  eval(fn).apply(window, args);
13504             }else{
13505                 /** eval:var:value */
13506                 return /** eval:var:value */ eval(fn).call(window, value);
13507             }
13508         },
13509
13510        
13511         /**
13512          * safer version of Math.toFixed..??/
13513          * @param {Number/String} value The numeric value to format
13514          * @param {Number/String} value Decimal places 
13515          * @return {String} The formatted currency string
13516          */
13517         toFixed : function(v, n)
13518         {
13519             // why not use to fixed - precision is buggered???
13520             if (!n) {
13521                 return Math.round(v-0);
13522             }
13523             var fact = Math.pow(10,n+1);
13524             v = (Math.round((v-0)*fact))/fact;
13525             var z = (''+fact).substring(2);
13526             if (v == Math.floor(v)) {
13527                 return Math.floor(v) + '.' + z;
13528             }
13529             
13530             // now just padd decimals..
13531             var ps = String(v).split('.');
13532             var fd = (ps[1] + z);
13533             var r = fd.substring(0,n); 
13534             var rm = fd.substring(n); 
13535             if (rm < 5) {
13536                 return ps[0] + '.' + r;
13537             }
13538             r*=1; // turn it into a number;
13539             r++;
13540             if (String(r).length != n) {
13541                 ps[0]*=1;
13542                 ps[0]++;
13543                 r = String(r).substring(1); // chop the end off.
13544             }
13545             
13546             return ps[0] + '.' + r;
13547              
13548         },
13549         
13550         /**
13551          * Format a number as US currency
13552          * @param {Number/String} value The numeric value to format
13553          * @return {String} The formatted currency string
13554          */
13555         usMoney : function(v){
13556             return '$' + Roo.util.Format.number(v);
13557         },
13558         
13559         /**
13560          * Format a number
13561          * eventually this should probably emulate php's number_format
13562          * @param {Number/String} value The numeric value to format
13563          * @param {Number} decimals number of decimal places
13564          * @return {String} The formatted currency string
13565          */
13566         number : function(v,decimals)
13567         {
13568             // multiply and round.
13569             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13570             var mul = Math.pow(10, decimals);
13571             var zero = String(mul).substring(1);
13572             v = (Math.round((v-0)*mul))/mul;
13573             
13574             // if it's '0' number.. then
13575             
13576             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13577             v = String(v);
13578             var ps = v.split('.');
13579             var whole = ps[0];
13580             
13581             
13582             var r = /(\d+)(\d{3})/;
13583             // add comma's
13584             while (r.test(whole)) {
13585                 whole = whole.replace(r, '$1' + ',' + '$2');
13586             }
13587             
13588             
13589             var sub = ps[1] ?
13590                     // has decimals..
13591                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13592                     // does not have decimals
13593                     (decimals ? ('.' + zero) : '');
13594             
13595             
13596             return whole + sub ;
13597         },
13598         
13599         /**
13600          * Parse a value into a formatted date using the specified format pattern.
13601          * @param {Mixed} value The value to format
13602          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13603          * @return {String} The formatted date string
13604          */
13605         date : function(v, format){
13606             if(!v){
13607                 return "";
13608             }
13609             if(!(v instanceof Date)){
13610                 v = new Date(Date.parse(v));
13611             }
13612             return v.dateFormat(format || Roo.util.Format.defaults.date);
13613         },
13614
13615         /**
13616          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13617          * @param {String} format Any valid date format string
13618          * @return {Function} The date formatting function
13619          */
13620         dateRenderer : function(format){
13621             return function(v){
13622                 return Roo.util.Format.date(v, format);  
13623             };
13624         },
13625
13626         // private
13627         stripTagsRE : /<\/?[^>]+>/gi,
13628         
13629         /**
13630          * Strips all HTML tags
13631          * @param {Mixed} value The text from which to strip tags
13632          * @return {String} The stripped text
13633          */
13634         stripTags : function(v){
13635             return !v ? v : String(v).replace(this.stripTagsRE, "");
13636         }
13637     };
13638 }();
13639 Roo.util.Format.defaults = {
13640     date : 'd/M/Y'
13641 };/*
13642  * Based on:
13643  * Ext JS Library 1.1.1
13644  * Copyright(c) 2006-2007, Ext JS, LLC.
13645  *
13646  * Originally Released Under LGPL - original licence link has changed is not relivant.
13647  *
13648  * Fork - LGPL
13649  * <script type="text/javascript">
13650  */
13651
13652
13653  
13654
13655 /**
13656  * @class Roo.MasterTemplate
13657  * @extends Roo.Template
13658  * Provides a template that can have child templates. The syntax is:
13659 <pre><code>
13660 var t = new Roo.MasterTemplate(
13661         '&lt;select name="{name}"&gt;',
13662                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13663         '&lt;/select&gt;'
13664 );
13665 t.add('options', {value: 'foo', text: 'bar'});
13666 // or you can add multiple child elements in one shot
13667 t.addAll('options', [
13668     {value: 'foo', text: 'bar'},
13669     {value: 'foo2', text: 'bar2'},
13670     {value: 'foo3', text: 'bar3'}
13671 ]);
13672 // then append, applying the master template values
13673 t.append('my-form', {name: 'my-select'});
13674 </code></pre>
13675 * A name attribute for the child template is not required if you have only one child
13676 * template or you want to refer to them by index.
13677  */
13678 Roo.MasterTemplate = function(){
13679     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13680     this.originalHtml = this.html;
13681     var st = {};
13682     var m, re = this.subTemplateRe;
13683     re.lastIndex = 0;
13684     var subIndex = 0;
13685     while(m = re.exec(this.html)){
13686         var name = m[1], content = m[2];
13687         st[subIndex] = {
13688             name: name,
13689             index: subIndex,
13690             buffer: [],
13691             tpl : new Roo.Template(content)
13692         };
13693         if(name){
13694             st[name] = st[subIndex];
13695         }
13696         st[subIndex].tpl.compile();
13697         st[subIndex].tpl.call = this.call.createDelegate(this);
13698         subIndex++;
13699     }
13700     this.subCount = subIndex;
13701     this.subs = st;
13702 };
13703 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13704     /**
13705     * The regular expression used to match sub templates
13706     * @type RegExp
13707     * @property
13708     */
13709     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13710
13711     /**
13712      * Applies the passed values to a child template.
13713      * @param {String/Number} name (optional) The name or index of the child template
13714      * @param {Array/Object} values The values to be applied to the template
13715      * @return {MasterTemplate} this
13716      */
13717      add : function(name, values){
13718         if(arguments.length == 1){
13719             values = arguments[0];
13720             name = 0;
13721         }
13722         var s = this.subs[name];
13723         s.buffer[s.buffer.length] = s.tpl.apply(values);
13724         return this;
13725     },
13726
13727     /**
13728      * Applies all the passed values to a child template.
13729      * @param {String/Number} name (optional) The name or index of the child template
13730      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13731      * @param {Boolean} reset (optional) True to reset the template first
13732      * @return {MasterTemplate} this
13733      */
13734     fill : function(name, values, reset){
13735         var a = arguments;
13736         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13737             values = a[0];
13738             name = 0;
13739             reset = a[1];
13740         }
13741         if(reset){
13742             this.reset();
13743         }
13744         for(var i = 0, len = values.length; i < len; i++){
13745             this.add(name, values[i]);
13746         }
13747         return this;
13748     },
13749
13750     /**
13751      * Resets the template for reuse
13752      * @return {MasterTemplate} this
13753      */
13754      reset : function(){
13755         var s = this.subs;
13756         for(var i = 0; i < this.subCount; i++){
13757             s[i].buffer = [];
13758         }
13759         return this;
13760     },
13761
13762     applyTemplate : function(values){
13763         var s = this.subs;
13764         var replaceIndex = -1;
13765         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13766             return s[++replaceIndex].buffer.join("");
13767         });
13768         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13769     },
13770
13771     apply : function(){
13772         return this.applyTemplate.apply(this, arguments);
13773     },
13774
13775     compile : function(){return this;}
13776 });
13777
13778 /**
13779  * Alias for fill().
13780  * @method
13781  */
13782 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13783  /**
13784  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13785  * var tpl = Roo.MasterTemplate.from('element-id');
13786  * @param {String/HTMLElement} el
13787  * @param {Object} config
13788  * @static
13789  */
13790 Roo.MasterTemplate.from = function(el, config){
13791     el = Roo.getDom(el);
13792     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13793 };/*
13794  * Based on:
13795  * Ext JS Library 1.1.1
13796  * Copyright(c) 2006-2007, Ext JS, LLC.
13797  *
13798  * Originally Released Under LGPL - original licence link has changed is not relivant.
13799  *
13800  * Fork - LGPL
13801  * <script type="text/javascript">
13802  */
13803
13804  
13805 /**
13806  * @class Roo.util.CSS
13807  * Utility class for manipulating CSS rules
13808  * @singleton
13809  */
13810 Roo.util.CSS = function(){
13811         var rules = null;
13812         var doc = document;
13813
13814     var camelRe = /(-[a-z])/gi;
13815     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13816
13817    return {
13818    /**
13819     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13820     * tag and appended to the HEAD of the document.
13821     * @param {String|Object} cssText The text containing the css rules
13822     * @param {String} id An id to add to the stylesheet for later removal
13823     * @return {StyleSheet}
13824     */
13825     createStyleSheet : function(cssText, id){
13826         var ss;
13827         var head = doc.getElementsByTagName("head")[0];
13828         var nrules = doc.createElement("style");
13829         nrules.setAttribute("type", "text/css");
13830         if(id){
13831             nrules.setAttribute("id", id);
13832         }
13833         if (typeof(cssText) != 'string') {
13834             // support object maps..
13835             // not sure if this a good idea.. 
13836             // perhaps it should be merged with the general css handling
13837             // and handle js style props.
13838             var cssTextNew = [];
13839             for(var n in cssText) {
13840                 var citems = [];
13841                 for(var k in cssText[n]) {
13842                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13843                 }
13844                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13845                 
13846             }
13847             cssText = cssTextNew.join("\n");
13848             
13849         }
13850        
13851        
13852        if(Roo.isIE){
13853            head.appendChild(nrules);
13854            ss = nrules.styleSheet;
13855            ss.cssText = cssText;
13856        }else{
13857            try{
13858                 nrules.appendChild(doc.createTextNode(cssText));
13859            }catch(e){
13860                nrules.cssText = cssText; 
13861            }
13862            head.appendChild(nrules);
13863            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13864        }
13865        this.cacheStyleSheet(ss);
13866        return ss;
13867    },
13868
13869    /**
13870     * Removes a style or link tag by id
13871     * @param {String} id The id of the tag
13872     */
13873    removeStyleSheet : function(id){
13874        var existing = doc.getElementById(id);
13875        if(existing){
13876            existing.parentNode.removeChild(existing);
13877        }
13878    },
13879
13880    /**
13881     * Dynamically swaps an existing stylesheet reference for a new one
13882     * @param {String} id The id of an existing link tag to remove
13883     * @param {String} url The href of the new stylesheet to include
13884     */
13885    swapStyleSheet : function(id, url){
13886        this.removeStyleSheet(id);
13887        var ss = doc.createElement("link");
13888        ss.setAttribute("rel", "stylesheet");
13889        ss.setAttribute("type", "text/css");
13890        ss.setAttribute("id", id);
13891        ss.setAttribute("href", url);
13892        doc.getElementsByTagName("head")[0].appendChild(ss);
13893    },
13894    
13895    /**
13896     * Refresh the rule cache if you have dynamically added stylesheets
13897     * @return {Object} An object (hash) of rules indexed by selector
13898     */
13899    refreshCache : function(){
13900        return this.getRules(true);
13901    },
13902
13903    // private
13904    cacheStyleSheet : function(stylesheet){
13905        if(!rules){
13906            rules = {};
13907        }
13908        try{// try catch for cross domain access issue
13909            var ssRules = stylesheet.cssRules || stylesheet.rules;
13910            for(var j = ssRules.length-1; j >= 0; --j){
13911                rules[ssRules[j].selectorText] = ssRules[j];
13912            }
13913        }catch(e){}
13914    },
13915    
13916    /**
13917     * Gets all css rules for the document
13918     * @param {Boolean} refreshCache true to refresh the internal cache
13919     * @return {Object} An object (hash) of rules indexed by selector
13920     */
13921    getRules : function(refreshCache){
13922                 if(rules == null || refreshCache){
13923                         rules = {};
13924                         var ds = doc.styleSheets;
13925                         for(var i =0, len = ds.length; i < len; i++){
13926                             try{
13927                         this.cacheStyleSheet(ds[i]);
13928                     }catch(e){} 
13929                 }
13930                 }
13931                 return rules;
13932         },
13933         
13934         /**
13935     * Gets an an individual CSS rule by selector(s)
13936     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13937     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13938     * @return {CSSRule} The CSS rule or null if one is not found
13939     */
13940    getRule : function(selector, refreshCache){
13941                 var rs = this.getRules(refreshCache);
13942                 if(!(selector instanceof Array)){
13943                     return rs[selector];
13944                 }
13945                 for(var i = 0; i < selector.length; i++){
13946                         if(rs[selector[i]]){
13947                                 return rs[selector[i]];
13948                         }
13949                 }
13950                 return null;
13951         },
13952         
13953         
13954         /**
13955     * Updates a rule property
13956     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13957     * @param {String} property The css property
13958     * @param {String} value The new value for the property
13959     * @return {Boolean} true If a rule was found and updated
13960     */
13961    updateRule : function(selector, property, value){
13962                 if(!(selector instanceof Array)){
13963                         var rule = this.getRule(selector);
13964                         if(rule){
13965                                 rule.style[property.replace(camelRe, camelFn)] = value;
13966                                 return true;
13967                         }
13968                 }else{
13969                         for(var i = 0; i < selector.length; i++){
13970                                 if(this.updateRule(selector[i], property, value)){
13971                                         return true;
13972                                 }
13973                         }
13974                 }
13975                 return false;
13976         }
13977    };   
13978 }();/*
13979  * Based on:
13980  * Ext JS Library 1.1.1
13981  * Copyright(c) 2006-2007, Ext JS, LLC.
13982  *
13983  * Originally Released Under LGPL - original licence link has changed is not relivant.
13984  *
13985  * Fork - LGPL
13986  * <script type="text/javascript">
13987  */
13988
13989  
13990
13991 /**
13992  * @class Roo.util.ClickRepeater
13993  * @extends Roo.util.Observable
13994  * 
13995  * A wrapper class which can be applied to any element. Fires a "click" event while the
13996  * mouse is pressed. The interval between firings may be specified in the config but
13997  * defaults to 10 milliseconds.
13998  * 
13999  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14000  * 
14001  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14002  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14003  * Similar to an autorepeat key delay.
14004  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14005  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14006  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14007  *           "interval" and "delay" are ignored. "immediate" is honored.
14008  * @cfg {Boolean} preventDefault True to prevent the default click event
14009  * @cfg {Boolean} stopDefault True to stop the default click event
14010  * 
14011  * @history
14012  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14013  *     2007-02-02 jvs Renamed to ClickRepeater
14014  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14015  *
14016  *  @constructor
14017  * @param {String/HTMLElement/Element} el The element to listen on
14018  * @param {Object} config
14019  **/
14020 Roo.util.ClickRepeater = function(el, config)
14021 {
14022     this.el = Roo.get(el);
14023     this.el.unselectable();
14024
14025     Roo.apply(this, config);
14026
14027     this.addEvents({
14028     /**
14029      * @event mousedown
14030      * Fires when the mouse button is depressed.
14031      * @param {Roo.util.ClickRepeater} this
14032      */
14033         "mousedown" : true,
14034     /**
14035      * @event click
14036      * Fires on a specified interval during the time the element is pressed.
14037      * @param {Roo.util.ClickRepeater} this
14038      */
14039         "click" : true,
14040     /**
14041      * @event mouseup
14042      * Fires when the mouse key is released.
14043      * @param {Roo.util.ClickRepeater} this
14044      */
14045         "mouseup" : true
14046     });
14047
14048     this.el.on("mousedown", this.handleMouseDown, this);
14049     if(this.preventDefault || this.stopDefault){
14050         this.el.on("click", function(e){
14051             if(this.preventDefault){
14052                 e.preventDefault();
14053             }
14054             if(this.stopDefault){
14055                 e.stopEvent();
14056             }
14057         }, this);
14058     }
14059
14060     // allow inline handler
14061     if(this.handler){
14062         this.on("click", this.handler,  this.scope || this);
14063     }
14064
14065     Roo.util.ClickRepeater.superclass.constructor.call(this);
14066 };
14067
14068 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14069     interval : 20,
14070     delay: 250,
14071     preventDefault : true,
14072     stopDefault : false,
14073     timer : 0,
14074
14075     // private
14076     handleMouseDown : function(){
14077         clearTimeout(this.timer);
14078         this.el.blur();
14079         if(this.pressClass){
14080             this.el.addClass(this.pressClass);
14081         }
14082         this.mousedownTime = new Date();
14083
14084         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14085         this.el.on("mouseout", this.handleMouseOut, this);
14086
14087         this.fireEvent("mousedown", this);
14088         this.fireEvent("click", this);
14089         
14090         this.timer = this.click.defer(this.delay || this.interval, this);
14091     },
14092
14093     // private
14094     click : function(){
14095         this.fireEvent("click", this);
14096         this.timer = this.click.defer(this.getInterval(), this);
14097     },
14098
14099     // private
14100     getInterval: function(){
14101         if(!this.accelerate){
14102             return this.interval;
14103         }
14104         var pressTime = this.mousedownTime.getElapsed();
14105         if(pressTime < 500){
14106             return 400;
14107         }else if(pressTime < 1700){
14108             return 320;
14109         }else if(pressTime < 2600){
14110             return 250;
14111         }else if(pressTime < 3500){
14112             return 180;
14113         }else if(pressTime < 4400){
14114             return 140;
14115         }else if(pressTime < 5300){
14116             return 80;
14117         }else if(pressTime < 6200){
14118             return 50;
14119         }else{
14120             return 10;
14121         }
14122     },
14123
14124     // private
14125     handleMouseOut : function(){
14126         clearTimeout(this.timer);
14127         if(this.pressClass){
14128             this.el.removeClass(this.pressClass);
14129         }
14130         this.el.on("mouseover", this.handleMouseReturn, this);
14131     },
14132
14133     // private
14134     handleMouseReturn : function(){
14135         this.el.un("mouseover", this.handleMouseReturn);
14136         if(this.pressClass){
14137             this.el.addClass(this.pressClass);
14138         }
14139         this.click();
14140     },
14141
14142     // private
14143     handleMouseUp : function(){
14144         clearTimeout(this.timer);
14145         this.el.un("mouseover", this.handleMouseReturn);
14146         this.el.un("mouseout", this.handleMouseOut);
14147         Roo.get(document).un("mouseup", this.handleMouseUp);
14148         this.el.removeClass(this.pressClass);
14149         this.fireEvent("mouseup", this);
14150     }
14151 });/*
14152  * Based on:
14153  * Ext JS Library 1.1.1
14154  * Copyright(c) 2006-2007, Ext JS, LLC.
14155  *
14156  * Originally Released Under LGPL - original licence link has changed is not relivant.
14157  *
14158  * Fork - LGPL
14159  * <script type="text/javascript">
14160  */
14161
14162  
14163 /**
14164  * @class Roo.KeyNav
14165  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14166  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14167  * way to implement custom navigation schemes for any UI component.</p>
14168  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14169  * pageUp, pageDown, del, home, end.  Usage:</p>
14170  <pre><code>
14171 var nav = new Roo.KeyNav("my-element", {
14172     "left" : function(e){
14173         this.moveLeft(e.ctrlKey);
14174     },
14175     "right" : function(e){
14176         this.moveRight(e.ctrlKey);
14177     },
14178     "enter" : function(e){
14179         this.save();
14180     },
14181     scope : this
14182 });
14183 </code></pre>
14184  * @constructor
14185  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14186  * @param {Object} config The config
14187  */
14188 Roo.KeyNav = function(el, config){
14189     this.el = Roo.get(el);
14190     Roo.apply(this, config);
14191     if(!this.disabled){
14192         this.disabled = true;
14193         this.enable();
14194     }
14195 };
14196
14197 Roo.KeyNav.prototype = {
14198     /**
14199      * @cfg {Boolean} disabled
14200      * True to disable this KeyNav instance (defaults to false)
14201      */
14202     disabled : false,
14203     /**
14204      * @cfg {String} defaultEventAction
14205      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14206      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14207      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14208      */
14209     defaultEventAction: "stopEvent",
14210     /**
14211      * @cfg {Boolean} forceKeyDown
14212      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14213      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14214      * handle keydown instead of keypress.
14215      */
14216     forceKeyDown : false,
14217
14218     // private
14219     prepareEvent : function(e){
14220         var k = e.getKey();
14221         var h = this.keyToHandler[k];
14222         //if(h && this[h]){
14223         //    e.stopPropagation();
14224         //}
14225         if(Roo.isSafari && h && k >= 37 && k <= 40){
14226             e.stopEvent();
14227         }
14228     },
14229
14230     // private
14231     relay : function(e){
14232         var k = e.getKey();
14233         var h = this.keyToHandler[k];
14234         if(h && this[h]){
14235             if(this.doRelay(e, this[h], h) !== true){
14236                 e[this.defaultEventAction]();
14237             }
14238         }
14239     },
14240
14241     // private
14242     doRelay : function(e, h, hname){
14243         return h.call(this.scope || this, e);
14244     },
14245
14246     // possible handlers
14247     enter : false,
14248     left : false,
14249     right : false,
14250     up : false,
14251     down : false,
14252     tab : false,
14253     esc : false,
14254     pageUp : false,
14255     pageDown : false,
14256     del : false,
14257     home : false,
14258     end : false,
14259
14260     // quick lookup hash
14261     keyToHandler : {
14262         37 : "left",
14263         39 : "right",
14264         38 : "up",
14265         40 : "down",
14266         33 : "pageUp",
14267         34 : "pageDown",
14268         46 : "del",
14269         36 : "home",
14270         35 : "end",
14271         13 : "enter",
14272         27 : "esc",
14273         9  : "tab"
14274     },
14275
14276         /**
14277          * Enable this KeyNav
14278          */
14279         enable: function(){
14280                 if(this.disabled){
14281             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14282             // the EventObject will normalize Safari automatically
14283             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14284                 this.el.on("keydown", this.relay,  this);
14285             }else{
14286                 this.el.on("keydown", this.prepareEvent,  this);
14287                 this.el.on("keypress", this.relay,  this);
14288             }
14289                     this.disabled = false;
14290                 }
14291         },
14292
14293         /**
14294          * Disable this KeyNav
14295          */
14296         disable: function(){
14297                 if(!this.disabled){
14298                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14299                 this.el.un("keydown", this.relay);
14300             }else{
14301                 this.el.un("keydown", this.prepareEvent);
14302                 this.el.un("keypress", this.relay);
14303             }
14304                     this.disabled = true;
14305                 }
14306         }
14307 };/*
14308  * Based on:
14309  * Ext JS Library 1.1.1
14310  * Copyright(c) 2006-2007, Ext JS, LLC.
14311  *
14312  * Originally Released Under LGPL - original licence link has changed is not relivant.
14313  *
14314  * Fork - LGPL
14315  * <script type="text/javascript">
14316  */
14317
14318  
14319 /**
14320  * @class Roo.KeyMap
14321  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14322  * The constructor accepts the same config object as defined by {@link #addBinding}.
14323  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14324  * combination it will call the function with this signature (if the match is a multi-key
14325  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14326  * A KeyMap can also handle a string representation of keys.<br />
14327  * Usage:
14328  <pre><code>
14329 // map one key by key code
14330 var map = new Roo.KeyMap("my-element", {
14331     key: 13, // or Roo.EventObject.ENTER
14332     fn: myHandler,
14333     scope: myObject
14334 });
14335
14336 // map multiple keys to one action by string
14337 var map = new Roo.KeyMap("my-element", {
14338     key: "a\r\n\t",
14339     fn: myHandler,
14340     scope: myObject
14341 });
14342
14343 // map multiple keys to multiple actions by strings and array of codes
14344 var map = new Roo.KeyMap("my-element", [
14345     {
14346         key: [10,13],
14347         fn: function(){ alert("Return was pressed"); }
14348     }, {
14349         key: "abc",
14350         fn: function(){ alert('a, b or c was pressed'); }
14351     }, {
14352         key: "\t",
14353         ctrl:true,
14354         shift:true,
14355         fn: function(){ alert('Control + shift + tab was pressed.'); }
14356     }
14357 ]);
14358 </code></pre>
14359  * <b>Note: A KeyMap starts enabled</b>
14360  * @constructor
14361  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14362  * @param {Object} config The config (see {@link #addBinding})
14363  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14364  */
14365 Roo.KeyMap = function(el, config, eventName){
14366     this.el  = Roo.get(el);
14367     this.eventName = eventName || "keydown";
14368     this.bindings = [];
14369     if(config){
14370         this.addBinding(config);
14371     }
14372     this.enable();
14373 };
14374
14375 Roo.KeyMap.prototype = {
14376     /**
14377      * True to stop the event from bubbling and prevent the default browser action if the
14378      * key was handled by the KeyMap (defaults to false)
14379      * @type Boolean
14380      */
14381     stopEvent : false,
14382
14383     /**
14384      * Add a new binding to this KeyMap. The following config object properties are supported:
14385      * <pre>
14386 Property    Type             Description
14387 ----------  ---------------  ----------------------------------------------------------------------
14388 key         String/Array     A single keycode or an array of keycodes to handle
14389 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14390 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14391 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14392 fn          Function         The function to call when KeyMap finds the expected key combination
14393 scope       Object           The scope of the callback function
14394 </pre>
14395      *
14396      * Usage:
14397      * <pre><code>
14398 // Create a KeyMap
14399 var map = new Roo.KeyMap(document, {
14400     key: Roo.EventObject.ENTER,
14401     fn: handleKey,
14402     scope: this
14403 });
14404
14405 //Add a new binding to the existing KeyMap later
14406 map.addBinding({
14407     key: 'abc',
14408     shift: true,
14409     fn: handleKey,
14410     scope: this
14411 });
14412 </code></pre>
14413      * @param {Object/Array} config A single KeyMap config or an array of configs
14414      */
14415         addBinding : function(config){
14416         if(config instanceof Array){
14417             for(var i = 0, len = config.length; i < len; i++){
14418                 this.addBinding(config[i]);
14419             }
14420             return;
14421         }
14422         var keyCode = config.key,
14423             shift = config.shift, 
14424             ctrl = config.ctrl, 
14425             alt = config.alt,
14426             fn = config.fn,
14427             scope = config.scope;
14428         if(typeof keyCode == "string"){
14429             var ks = [];
14430             var keyString = keyCode.toUpperCase();
14431             for(var j = 0, len = keyString.length; j < len; j++){
14432                 ks.push(keyString.charCodeAt(j));
14433             }
14434             keyCode = ks;
14435         }
14436         var keyArray = keyCode instanceof Array;
14437         var handler = function(e){
14438             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14439                 var k = e.getKey();
14440                 if(keyArray){
14441                     for(var i = 0, len = keyCode.length; i < len; i++){
14442                         if(keyCode[i] == k){
14443                           if(this.stopEvent){
14444                               e.stopEvent();
14445                           }
14446                           fn.call(scope || window, k, e);
14447                           return;
14448                         }
14449                     }
14450                 }else{
14451                     if(k == keyCode){
14452                         if(this.stopEvent){
14453                            e.stopEvent();
14454                         }
14455                         fn.call(scope || window, k, e);
14456                     }
14457                 }
14458             }
14459         };
14460         this.bindings.push(handler);  
14461         },
14462
14463     /**
14464      * Shorthand for adding a single key listener
14465      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14466      * following options:
14467      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14468      * @param {Function} fn The function to call
14469      * @param {Object} scope (optional) The scope of the function
14470      */
14471     on : function(key, fn, scope){
14472         var keyCode, shift, ctrl, alt;
14473         if(typeof key == "object" && !(key instanceof Array)){
14474             keyCode = key.key;
14475             shift = key.shift;
14476             ctrl = key.ctrl;
14477             alt = key.alt;
14478         }else{
14479             keyCode = key;
14480         }
14481         this.addBinding({
14482             key: keyCode,
14483             shift: shift,
14484             ctrl: ctrl,
14485             alt: alt,
14486             fn: fn,
14487             scope: scope
14488         })
14489     },
14490
14491     // private
14492     handleKeyDown : function(e){
14493             if(this.enabled){ //just in case
14494             var b = this.bindings;
14495             for(var i = 0, len = b.length; i < len; i++){
14496                 b[i].call(this, e);
14497             }
14498             }
14499         },
14500         
14501         /**
14502          * Returns true if this KeyMap is enabled
14503          * @return {Boolean} 
14504          */
14505         isEnabled : function(){
14506             return this.enabled;  
14507         },
14508         
14509         /**
14510          * Enables this KeyMap
14511          */
14512         enable: function(){
14513                 if(!this.enabled){
14514                     this.el.on(this.eventName, this.handleKeyDown, this);
14515                     this.enabled = true;
14516                 }
14517         },
14518
14519         /**
14520          * Disable this KeyMap
14521          */
14522         disable: function(){
14523                 if(this.enabled){
14524                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14525                     this.enabled = false;
14526                 }
14527         }
14528 };/*
14529  * Based on:
14530  * Ext JS Library 1.1.1
14531  * Copyright(c) 2006-2007, Ext JS, LLC.
14532  *
14533  * Originally Released Under LGPL - original licence link has changed is not relivant.
14534  *
14535  * Fork - LGPL
14536  * <script type="text/javascript">
14537  */
14538
14539  
14540 /**
14541  * @class Roo.util.TextMetrics
14542  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14543  * wide, in pixels, a given block of text will be.
14544  * @singleton
14545  */
14546 Roo.util.TextMetrics = function(){
14547     var shared;
14548     return {
14549         /**
14550          * Measures the size of the specified text
14551          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14552          * that can affect the size of the rendered text
14553          * @param {String} text The text to measure
14554          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14555          * in order to accurately measure the text height
14556          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14557          */
14558         measure : function(el, text, fixedWidth){
14559             if(!shared){
14560                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14561             }
14562             shared.bind(el);
14563             shared.setFixedWidth(fixedWidth || 'auto');
14564             return shared.getSize(text);
14565         },
14566
14567         /**
14568          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14569          * the overhead of multiple calls to initialize the style properties on each measurement.
14570          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14571          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14572          * in order to accurately measure the text height
14573          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14574          */
14575         createInstance : function(el, fixedWidth){
14576             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14577         }
14578     };
14579 }();
14580
14581  
14582
14583 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14584     var ml = new Roo.Element(document.createElement('div'));
14585     document.body.appendChild(ml.dom);
14586     ml.position('absolute');
14587     ml.setLeftTop(-1000, -1000);
14588     ml.hide();
14589
14590     if(fixedWidth){
14591         ml.setWidth(fixedWidth);
14592     }
14593      
14594     var instance = {
14595         /**
14596          * Returns the size of the specified text based on the internal element's style and width properties
14597          * @memberOf Roo.util.TextMetrics.Instance#
14598          * @param {String} text The text to measure
14599          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14600          */
14601         getSize : function(text){
14602             ml.update(text);
14603             var s = ml.getSize();
14604             ml.update('');
14605             return s;
14606         },
14607
14608         /**
14609          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14610          * that can affect the size of the rendered text
14611          * @memberOf Roo.util.TextMetrics.Instance#
14612          * @param {String/HTMLElement} el The element, dom node or id
14613          */
14614         bind : function(el){
14615             ml.setStyle(
14616                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14617             );
14618         },
14619
14620         /**
14621          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14622          * to set a fixed width in order to accurately measure the text height.
14623          * @memberOf Roo.util.TextMetrics.Instance#
14624          * @param {Number} width The width to set on the element
14625          */
14626         setFixedWidth : function(width){
14627             ml.setWidth(width);
14628         },
14629
14630         /**
14631          * Returns the measured width of the specified text
14632          * @memberOf Roo.util.TextMetrics.Instance#
14633          * @param {String} text The text to measure
14634          * @return {Number} width The width in pixels
14635          */
14636         getWidth : function(text){
14637             ml.dom.style.width = 'auto';
14638             return this.getSize(text).width;
14639         },
14640
14641         /**
14642          * Returns the measured height of the specified text.  For multiline text, be sure to call
14643          * {@link #setFixedWidth} if necessary.
14644          * @memberOf Roo.util.TextMetrics.Instance#
14645          * @param {String} text The text to measure
14646          * @return {Number} height The height in pixels
14647          */
14648         getHeight : function(text){
14649             return this.getSize(text).height;
14650         }
14651     };
14652
14653     instance.bind(bindTo);
14654
14655     return instance;
14656 };
14657
14658 // backwards compat
14659 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14660  * Based on:
14661  * Ext JS Library 1.1.1
14662  * Copyright(c) 2006-2007, Ext JS, LLC.
14663  *
14664  * Originally Released Under LGPL - original licence link has changed is not relivant.
14665  *
14666  * Fork - LGPL
14667  * <script type="text/javascript">
14668  */
14669
14670 /**
14671  * @class Roo.state.Provider
14672  * Abstract base class for state provider implementations. This class provides methods
14673  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14674  * Provider interface.
14675  */
14676 Roo.state.Provider = function(){
14677     /**
14678      * @event statechange
14679      * Fires when a state change occurs.
14680      * @param {Provider} this This state provider
14681      * @param {String} key The state key which was changed
14682      * @param {String} value The encoded value for the state
14683      */
14684     this.addEvents({
14685         "statechange": true
14686     });
14687     this.state = {};
14688     Roo.state.Provider.superclass.constructor.call(this);
14689 };
14690 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14691     /**
14692      * Returns the current value for a key
14693      * @param {String} name The key name
14694      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14695      * @return {Mixed} The state data
14696      */
14697     get : function(name, defaultValue){
14698         return typeof this.state[name] == "undefined" ?
14699             defaultValue : this.state[name];
14700     },
14701     
14702     /**
14703      * Clears a value from the state
14704      * @param {String} name The key name
14705      */
14706     clear : function(name){
14707         delete this.state[name];
14708         this.fireEvent("statechange", this, name, null);
14709     },
14710     
14711     /**
14712      * Sets the value for a key
14713      * @param {String} name The key name
14714      * @param {Mixed} value The value to set
14715      */
14716     set : function(name, value){
14717         this.state[name] = value;
14718         this.fireEvent("statechange", this, name, value);
14719     },
14720     
14721     /**
14722      * Decodes a string previously encoded with {@link #encodeValue}.
14723      * @param {String} value The value to decode
14724      * @return {Mixed} The decoded value
14725      */
14726     decodeValue : function(cookie){
14727         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14728         var matches = re.exec(unescape(cookie));
14729         if(!matches || !matches[1]) return; // non state cookie
14730         var type = matches[1];
14731         var v = matches[2];
14732         switch(type){
14733             case "n":
14734                 return parseFloat(v);
14735             case "d":
14736                 return new Date(Date.parse(v));
14737             case "b":
14738                 return (v == "1");
14739             case "a":
14740                 var all = [];
14741                 var values = v.split("^");
14742                 for(var i = 0, len = values.length; i < len; i++){
14743                     all.push(this.decodeValue(values[i]));
14744                 }
14745                 return all;
14746            case "o":
14747                 var all = {};
14748                 var values = v.split("^");
14749                 for(var i = 0, len = values.length; i < len; i++){
14750                     var kv = values[i].split("=");
14751                     all[kv[0]] = this.decodeValue(kv[1]);
14752                 }
14753                 return all;
14754            default:
14755                 return v;
14756         }
14757     },
14758     
14759     /**
14760      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14761      * @param {Mixed} value The value to encode
14762      * @return {String} The encoded value
14763      */
14764     encodeValue : function(v){
14765         var enc;
14766         if(typeof v == "number"){
14767             enc = "n:" + v;
14768         }else if(typeof v == "boolean"){
14769             enc = "b:" + (v ? "1" : "0");
14770         }else if(v instanceof Date){
14771             enc = "d:" + v.toGMTString();
14772         }else if(v instanceof Array){
14773             var flat = "";
14774             for(var i = 0, len = v.length; i < len; i++){
14775                 flat += this.encodeValue(v[i]);
14776                 if(i != len-1) flat += "^";
14777             }
14778             enc = "a:" + flat;
14779         }else if(typeof v == "object"){
14780             var flat = "";
14781             for(var key in v){
14782                 if(typeof v[key] != "function"){
14783                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14784                 }
14785             }
14786             enc = "o:" + flat.substring(0, flat.length-1);
14787         }else{
14788             enc = "s:" + v;
14789         }
14790         return escape(enc);        
14791     }
14792 });
14793
14794 /*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804 /**
14805  * @class Roo.state.Manager
14806  * This is the global state manager. By default all components that are "state aware" check this class
14807  * for state information if you don't pass them a custom state provider. In order for this class
14808  * to be useful, it must be initialized with a provider when your application initializes.
14809  <pre><code>
14810 // in your initialization function
14811 init : function(){
14812    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14813    ...
14814    // supposed you have a {@link Roo.BorderLayout}
14815    var layout = new Roo.BorderLayout(...);
14816    layout.restoreState();
14817    // or a {Roo.BasicDialog}
14818    var dialog = new Roo.BasicDialog(...);
14819    dialog.restoreState();
14820  </code></pre>
14821  * @singleton
14822  */
14823 Roo.state.Manager = function(){
14824     var provider = new Roo.state.Provider();
14825     
14826     return {
14827         /**
14828          * Configures the default state provider for your application
14829          * @param {Provider} stateProvider The state provider to set
14830          */
14831         setProvider : function(stateProvider){
14832             provider = stateProvider;
14833         },
14834         
14835         /**
14836          * Returns the current value for a key
14837          * @param {String} name The key name
14838          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14839          * @return {Mixed} The state data
14840          */
14841         get : function(key, defaultValue){
14842             return provider.get(key, defaultValue);
14843         },
14844         
14845         /**
14846          * Sets the value for a key
14847          * @param {String} name The key name
14848          * @param {Mixed} value The state data
14849          */
14850          set : function(key, value){
14851             provider.set(key, value);
14852         },
14853         
14854         /**
14855          * Clears a value from the state
14856          * @param {String} name The key name
14857          */
14858         clear : function(key){
14859             provider.clear(key);
14860         },
14861         
14862         /**
14863          * Gets the currently configured state provider
14864          * @return {Provider} The state provider
14865          */
14866         getProvider : function(){
14867             return provider;
14868         }
14869     };
14870 }();
14871 /*
14872  * Based on:
14873  * Ext JS Library 1.1.1
14874  * Copyright(c) 2006-2007, Ext JS, LLC.
14875  *
14876  * Originally Released Under LGPL - original licence link has changed is not relivant.
14877  *
14878  * Fork - LGPL
14879  * <script type="text/javascript">
14880  */
14881 /**
14882  * @class Roo.state.CookieProvider
14883  * @extends Roo.state.Provider
14884  * The default Provider implementation which saves state via cookies.
14885  * <br />Usage:
14886  <pre><code>
14887    var cp = new Roo.state.CookieProvider({
14888        path: "/cgi-bin/",
14889        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14890        domain: "roojs.com"
14891    })
14892    Roo.state.Manager.setProvider(cp);
14893  </code></pre>
14894  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14895  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14896  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14897  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14898  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14899  * domain the page is running on including the 'www' like 'www.roojs.com')
14900  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14901  * @constructor
14902  * Create a new CookieProvider
14903  * @param {Object} config The configuration object
14904  */
14905 Roo.state.CookieProvider = function(config){
14906     Roo.state.CookieProvider.superclass.constructor.call(this);
14907     this.path = "/";
14908     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14909     this.domain = null;
14910     this.secure = false;
14911     Roo.apply(this, config);
14912     this.state = this.readCookies();
14913 };
14914
14915 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14916     // private
14917     set : function(name, value){
14918         if(typeof value == "undefined" || value === null){
14919             this.clear(name);
14920             return;
14921         }
14922         this.setCookie(name, value);
14923         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14924     },
14925
14926     // private
14927     clear : function(name){
14928         this.clearCookie(name);
14929         Roo.state.CookieProvider.superclass.clear.call(this, name);
14930     },
14931
14932     // private
14933     readCookies : function(){
14934         var cookies = {};
14935         var c = document.cookie + ";";
14936         var re = /\s?(.*?)=(.*?);/g;
14937         var matches;
14938         while((matches = re.exec(c)) != null){
14939             var name = matches[1];
14940             var value = matches[2];
14941             if(name && name.substring(0,3) == "ys-"){
14942                 cookies[name.substr(3)] = this.decodeValue(value);
14943             }
14944         }
14945         return cookies;
14946     },
14947
14948     // private
14949     setCookie : function(name, value){
14950         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14951            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14952            ((this.path == null) ? "" : ("; path=" + this.path)) +
14953            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14954            ((this.secure == true) ? "; secure" : "");
14955     },
14956
14957     // private
14958     clearCookie : function(name){
14959         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14960            ((this.path == null) ? "" : ("; path=" + this.path)) +
14961            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14962            ((this.secure == true) ? "; secure" : "");
14963     }
14964 });/*
14965  * Based on:
14966  * Ext JS Library 1.1.1
14967  * Copyright(c) 2006-2007, Ext JS, LLC.
14968  *
14969  * Originally Released Under LGPL - original licence link has changed is not relivant.
14970  *
14971  * Fork - LGPL
14972  * <script type="text/javascript">
14973  */
14974  
14975
14976 /**
14977  * @class Roo.ComponentMgr
14978  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14979  * @singleton
14980  */
14981 Roo.ComponentMgr = function(){
14982     var all = new Roo.util.MixedCollection();
14983
14984     return {
14985         /**
14986          * Registers a component.
14987          * @param {Roo.Component} c The component
14988          */
14989         register : function(c){
14990             all.add(c);
14991         },
14992
14993         /**
14994          * Unregisters a component.
14995          * @param {Roo.Component} c The component
14996          */
14997         unregister : function(c){
14998             all.remove(c);
14999         },
15000
15001         /**
15002          * Returns a component by id
15003          * @param {String} id The component id
15004          */
15005         get : function(id){
15006             return all.get(id);
15007         },
15008
15009         /**
15010          * Registers a function that will be called when a specified component is added to ComponentMgr
15011          * @param {String} id The component id
15012          * @param {Funtction} fn The callback function
15013          * @param {Object} scope The scope of the callback
15014          */
15015         onAvailable : function(id, fn, scope){
15016             all.on("add", function(index, o){
15017                 if(o.id == id){
15018                     fn.call(scope || o, o);
15019                     all.un("add", fn, scope);
15020                 }
15021             });
15022         }
15023     };
15024 }();/*
15025  * Based on:
15026  * Ext JS Library 1.1.1
15027  * Copyright(c) 2006-2007, Ext JS, LLC.
15028  *
15029  * Originally Released Under LGPL - original licence link has changed is not relivant.
15030  *
15031  * Fork - LGPL
15032  * <script type="text/javascript">
15033  */
15034  
15035 /**
15036  * @class Roo.Component
15037  * @extends Roo.util.Observable
15038  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15039  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15040  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15041  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15042  * All visual components (widgets) that require rendering into a layout should subclass Component.
15043  * @constructor
15044  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15045  * 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
15046  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15047  */
15048 Roo.Component = function(config){
15049     config = config || {};
15050     if(config.tagName || config.dom || typeof config == "string"){ // element object
15051         config = {el: config, id: config.id || config};
15052     }
15053     this.initialConfig = config;
15054
15055     Roo.apply(this, config);
15056     this.addEvents({
15057         /**
15058          * @event disable
15059          * Fires after the component is disabled.
15060              * @param {Roo.Component} this
15061              */
15062         disable : true,
15063         /**
15064          * @event enable
15065          * Fires after the component is enabled.
15066              * @param {Roo.Component} this
15067              */
15068         enable : true,
15069         /**
15070          * @event beforeshow
15071          * Fires before the component is shown.  Return false to stop the show.
15072              * @param {Roo.Component} this
15073              */
15074         beforeshow : true,
15075         /**
15076          * @event show
15077          * Fires after the component is shown.
15078              * @param {Roo.Component} this
15079              */
15080         show : true,
15081         /**
15082          * @event beforehide
15083          * Fires before the component is hidden. Return false to stop the hide.
15084              * @param {Roo.Component} this
15085              */
15086         beforehide : true,
15087         /**
15088          * @event hide
15089          * Fires after the component is hidden.
15090              * @param {Roo.Component} this
15091              */
15092         hide : true,
15093         /**
15094          * @event beforerender
15095          * Fires before the component is rendered. Return false to stop the render.
15096              * @param {Roo.Component} this
15097              */
15098         beforerender : true,
15099         /**
15100          * @event render
15101          * Fires after the component is rendered.
15102              * @param {Roo.Component} this
15103              */
15104         render : true,
15105         /**
15106          * @event beforedestroy
15107          * Fires before the component is destroyed. Return false to stop the destroy.
15108              * @param {Roo.Component} this
15109              */
15110         beforedestroy : true,
15111         /**
15112          * @event destroy
15113          * Fires after the component is destroyed.
15114              * @param {Roo.Component} this
15115              */
15116         destroy : true
15117     });
15118     if(!this.id){
15119         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15120     }
15121     Roo.ComponentMgr.register(this);
15122     Roo.Component.superclass.constructor.call(this);
15123     this.initComponent();
15124     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15125         this.render(this.renderTo);
15126         delete this.renderTo;
15127     }
15128 };
15129
15130 /** @private */
15131 Roo.Component.AUTO_ID = 1000;
15132
15133 Roo.extend(Roo.Component, Roo.util.Observable, {
15134     /**
15135      * @scope Roo.Component.prototype
15136      * @type {Boolean}
15137      * true if this component is hidden. Read-only.
15138      */
15139     hidden : false,
15140     /**
15141      * @type {Boolean}
15142      * true if this component is disabled. Read-only.
15143      */
15144     disabled : false,
15145     /**
15146      * @type {Boolean}
15147      * true if this component has been rendered. Read-only.
15148      */
15149     rendered : false,
15150     
15151     /** @cfg {String} disableClass
15152      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15153      */
15154     disabledClass : "x-item-disabled",
15155         /** @cfg {Boolean} allowDomMove
15156          * Whether the component can move the Dom node when rendering (defaults to true).
15157          */
15158     allowDomMove : true,
15159     /** @cfg {String} hideMode
15160      * How this component should hidden. Supported values are
15161      * "visibility" (css visibility), "offsets" (negative offset position) and
15162      * "display" (css display) - defaults to "display".
15163      */
15164     hideMode: 'display',
15165
15166     /** @private */
15167     ctype : "Roo.Component",
15168
15169     /**
15170      * @cfg {String} actionMode 
15171      * which property holds the element that used for  hide() / show() / disable() / enable()
15172      * default is 'el' 
15173      */
15174     actionMode : "el",
15175
15176     /** @private */
15177     getActionEl : function(){
15178         return this[this.actionMode];
15179     },
15180
15181     initComponent : Roo.emptyFn,
15182     /**
15183      * If this is a lazy rendering component, render it to its container element.
15184      * @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.
15185      */
15186     render : function(container, position){
15187         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15188             if(!container && this.el){
15189                 this.el = Roo.get(this.el);
15190                 container = this.el.dom.parentNode;
15191                 this.allowDomMove = false;
15192             }
15193             this.container = Roo.get(container);
15194             this.rendered = true;
15195             if(position !== undefined){
15196                 if(typeof position == 'number'){
15197                     position = this.container.dom.childNodes[position];
15198                 }else{
15199                     position = Roo.getDom(position);
15200                 }
15201             }
15202             this.onRender(this.container, position || null);
15203             if(this.cls){
15204                 this.el.addClass(this.cls);
15205                 delete this.cls;
15206             }
15207             if(this.style){
15208                 this.el.applyStyles(this.style);
15209                 delete this.style;
15210             }
15211             this.fireEvent("render", this);
15212             this.afterRender(this.container);
15213             if(this.hidden){
15214                 this.hide();
15215             }
15216             if(this.disabled){
15217                 this.disable();
15218             }
15219         }
15220         return this;
15221     },
15222
15223     /** @private */
15224     // default function is not really useful
15225     onRender : function(ct, position){
15226         if(this.el){
15227             this.el = Roo.get(this.el);
15228             if(this.allowDomMove !== false){
15229                 ct.dom.insertBefore(this.el.dom, position);
15230             }
15231         }
15232     },
15233
15234     /** @private */
15235     getAutoCreate : function(){
15236         var cfg = typeof this.autoCreate == "object" ?
15237                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15238         if(this.id && !cfg.id){
15239             cfg.id = this.id;
15240         }
15241         return cfg;
15242     },
15243
15244     /** @private */
15245     afterRender : Roo.emptyFn,
15246
15247     /**
15248      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15249      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15250      */
15251     destroy : function(){
15252         if(this.fireEvent("beforedestroy", this) !== false){
15253             this.purgeListeners();
15254             this.beforeDestroy();
15255             if(this.rendered){
15256                 this.el.removeAllListeners();
15257                 this.el.remove();
15258                 if(this.actionMode == "container"){
15259                     this.container.remove();
15260                 }
15261             }
15262             this.onDestroy();
15263             Roo.ComponentMgr.unregister(this);
15264             this.fireEvent("destroy", this);
15265         }
15266     },
15267
15268         /** @private */
15269     beforeDestroy : function(){
15270
15271     },
15272
15273         /** @private */
15274         onDestroy : function(){
15275
15276     },
15277
15278     /**
15279      * Returns the underlying {@link Roo.Element}.
15280      * @return {Roo.Element} The element
15281      */
15282     getEl : function(){
15283         return this.el;
15284     },
15285
15286     /**
15287      * Returns the id of this component.
15288      * @return {String}
15289      */
15290     getId : function(){
15291         return this.id;
15292     },
15293
15294     /**
15295      * Try to focus this component.
15296      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15297      * @return {Roo.Component} this
15298      */
15299     focus : function(selectText){
15300         if(this.rendered){
15301             this.el.focus();
15302             if(selectText === true){
15303                 this.el.dom.select();
15304             }
15305         }
15306         return this;
15307     },
15308
15309     /** @private */
15310     blur : function(){
15311         if(this.rendered){
15312             this.el.blur();
15313         }
15314         return this;
15315     },
15316
15317     /**
15318      * Disable this component.
15319      * @return {Roo.Component} this
15320      */
15321     disable : function(){
15322         if(this.rendered){
15323             this.onDisable();
15324         }
15325         this.disabled = true;
15326         this.fireEvent("disable", this);
15327         return this;
15328     },
15329
15330         // private
15331     onDisable : function(){
15332         this.getActionEl().addClass(this.disabledClass);
15333         this.el.dom.disabled = true;
15334     },
15335
15336     /**
15337      * Enable this component.
15338      * @return {Roo.Component} this
15339      */
15340     enable : function(){
15341         if(this.rendered){
15342             this.onEnable();
15343         }
15344         this.disabled = false;
15345         this.fireEvent("enable", this);
15346         return this;
15347     },
15348
15349         // private
15350     onEnable : function(){
15351         this.getActionEl().removeClass(this.disabledClass);
15352         this.el.dom.disabled = false;
15353     },
15354
15355     /**
15356      * Convenience function for setting disabled/enabled by boolean.
15357      * @param {Boolean} disabled
15358      */
15359     setDisabled : function(disabled){
15360         this[disabled ? "disable" : "enable"]();
15361     },
15362
15363     /**
15364      * Show this component.
15365      * @return {Roo.Component} this
15366      */
15367     show: function(){
15368         if(this.fireEvent("beforeshow", this) !== false){
15369             this.hidden = false;
15370             if(this.rendered){
15371                 this.onShow();
15372             }
15373             this.fireEvent("show", this);
15374         }
15375         return this;
15376     },
15377
15378     // private
15379     onShow : function(){
15380         var ae = this.getActionEl();
15381         if(this.hideMode == 'visibility'){
15382             ae.dom.style.visibility = "visible";
15383         }else if(this.hideMode == 'offsets'){
15384             ae.removeClass('x-hidden');
15385         }else{
15386             ae.dom.style.display = "";
15387         }
15388     },
15389
15390     /**
15391      * Hide this component.
15392      * @return {Roo.Component} this
15393      */
15394     hide: function(){
15395         if(this.fireEvent("beforehide", this) !== false){
15396             this.hidden = true;
15397             if(this.rendered){
15398                 this.onHide();
15399             }
15400             this.fireEvent("hide", this);
15401         }
15402         return this;
15403     },
15404
15405     // private
15406     onHide : function(){
15407         var ae = this.getActionEl();
15408         if(this.hideMode == 'visibility'){
15409             ae.dom.style.visibility = "hidden";
15410         }else if(this.hideMode == 'offsets'){
15411             ae.addClass('x-hidden');
15412         }else{
15413             ae.dom.style.display = "none";
15414         }
15415     },
15416
15417     /**
15418      * Convenience function to hide or show this component by boolean.
15419      * @param {Boolean} visible True to show, false to hide
15420      * @return {Roo.Component} this
15421      */
15422     setVisible: function(visible){
15423         if(visible) {
15424             this.show();
15425         }else{
15426             this.hide();
15427         }
15428         return this;
15429     },
15430
15431     /**
15432      * Returns true if this component is visible.
15433      */
15434     isVisible : function(){
15435         return this.getActionEl().isVisible();
15436     },
15437
15438     cloneConfig : function(overrides){
15439         overrides = overrides || {};
15440         var id = overrides.id || Roo.id();
15441         var cfg = Roo.applyIf(overrides, this.initialConfig);
15442         cfg.id = id; // prevent dup id
15443         return new this.constructor(cfg);
15444     }
15445 });/*
15446  * Based on:
15447  * Ext JS Library 1.1.1
15448  * Copyright(c) 2006-2007, Ext JS, LLC.
15449  *
15450  * Originally Released Under LGPL - original licence link has changed is not relivant.
15451  *
15452  * Fork - LGPL
15453  * <script type="text/javascript">
15454  */
15455
15456 /**
15457  * @class Roo.BoxComponent
15458  * @extends Roo.Component
15459  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15460  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15461  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15462  * layout containers.
15463  * @constructor
15464  * @param {Roo.Element/String/Object} config The configuration options.
15465  */
15466 Roo.BoxComponent = function(config){
15467     Roo.Component.call(this, config);
15468     this.addEvents({
15469         /**
15470          * @event resize
15471          * Fires after the component is resized.
15472              * @param {Roo.Component} this
15473              * @param {Number} adjWidth The box-adjusted width that was set
15474              * @param {Number} adjHeight The box-adjusted height that was set
15475              * @param {Number} rawWidth The width that was originally specified
15476              * @param {Number} rawHeight The height that was originally specified
15477              */
15478         resize : true,
15479         /**
15480          * @event move
15481          * Fires after the component is moved.
15482              * @param {Roo.Component} this
15483              * @param {Number} x The new x position
15484              * @param {Number} y The new y position
15485              */
15486         move : true
15487     });
15488 };
15489
15490 Roo.extend(Roo.BoxComponent, Roo.Component, {
15491     // private, set in afterRender to signify that the component has been rendered
15492     boxReady : false,
15493     // private, used to defer height settings to subclasses
15494     deferHeight: false,
15495     /** @cfg {Number} width
15496      * width (optional) size of component
15497      */
15498      /** @cfg {Number} height
15499      * height (optional) size of component
15500      */
15501      
15502     /**
15503      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15504      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15505      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15506      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15507      * @return {Roo.BoxComponent} this
15508      */
15509     setSize : function(w, h){
15510         // support for standard size objects
15511         if(typeof w == 'object'){
15512             h = w.height;
15513             w = w.width;
15514         }
15515         // not rendered
15516         if(!this.boxReady){
15517             this.width = w;
15518             this.height = h;
15519             return this;
15520         }
15521
15522         // prevent recalcs when not needed
15523         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15524             return this;
15525         }
15526         this.lastSize = {width: w, height: h};
15527
15528         var adj = this.adjustSize(w, h);
15529         var aw = adj.width, ah = adj.height;
15530         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15531             var rz = this.getResizeEl();
15532             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15533                 rz.setSize(aw, ah);
15534             }else if(!this.deferHeight && ah !== undefined){
15535                 rz.setHeight(ah);
15536             }else if(aw !== undefined){
15537                 rz.setWidth(aw);
15538             }
15539             this.onResize(aw, ah, w, h);
15540             this.fireEvent('resize', this, aw, ah, w, h);
15541         }
15542         return this;
15543     },
15544
15545     /**
15546      * Gets the current size of the component's underlying element.
15547      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15548      */
15549     getSize : function(){
15550         return this.el.getSize();
15551     },
15552
15553     /**
15554      * Gets the current XY position of the component's underlying element.
15555      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15556      * @return {Array} The XY position of the element (e.g., [100, 200])
15557      */
15558     getPosition : function(local){
15559         if(local === true){
15560             return [this.el.getLeft(true), this.el.getTop(true)];
15561         }
15562         return this.xy || this.el.getXY();
15563     },
15564
15565     /**
15566      * Gets the current box measurements of the component's underlying element.
15567      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15568      * @returns {Object} box An object in the format {x, y, width, height}
15569      */
15570     getBox : function(local){
15571         var s = this.el.getSize();
15572         if(local){
15573             s.x = this.el.getLeft(true);
15574             s.y = this.el.getTop(true);
15575         }else{
15576             var xy = this.xy || this.el.getXY();
15577             s.x = xy[0];
15578             s.y = xy[1];
15579         }
15580         return s;
15581     },
15582
15583     /**
15584      * Sets the current box measurements of the component's underlying element.
15585      * @param {Object} box An object in the format {x, y, width, height}
15586      * @returns {Roo.BoxComponent} this
15587      */
15588     updateBox : function(box){
15589         this.setSize(box.width, box.height);
15590         this.setPagePosition(box.x, box.y);
15591         return this;
15592     },
15593
15594     // protected
15595     getResizeEl : function(){
15596         return this.resizeEl || this.el;
15597     },
15598
15599     // protected
15600     getPositionEl : function(){
15601         return this.positionEl || this.el;
15602     },
15603
15604     /**
15605      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15606      * This method fires the move event.
15607      * @param {Number} left The new left
15608      * @param {Number} top The new top
15609      * @returns {Roo.BoxComponent} this
15610      */
15611     setPosition : function(x, y){
15612         this.x = x;
15613         this.y = y;
15614         if(!this.boxReady){
15615             return this;
15616         }
15617         var adj = this.adjustPosition(x, y);
15618         var ax = adj.x, ay = adj.y;
15619
15620         var el = this.getPositionEl();
15621         if(ax !== undefined || ay !== undefined){
15622             if(ax !== undefined && ay !== undefined){
15623                 el.setLeftTop(ax, ay);
15624             }else if(ax !== undefined){
15625                 el.setLeft(ax);
15626             }else if(ay !== undefined){
15627                 el.setTop(ay);
15628             }
15629             this.onPosition(ax, ay);
15630             this.fireEvent('move', this, ax, ay);
15631         }
15632         return this;
15633     },
15634
15635     /**
15636      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15637      * This method fires the move event.
15638      * @param {Number} x The new x position
15639      * @param {Number} y The new y position
15640      * @returns {Roo.BoxComponent} this
15641      */
15642     setPagePosition : function(x, y){
15643         this.pageX = x;
15644         this.pageY = y;
15645         if(!this.boxReady){
15646             return;
15647         }
15648         if(x === undefined || y === undefined){ // cannot translate undefined points
15649             return;
15650         }
15651         var p = this.el.translatePoints(x, y);
15652         this.setPosition(p.left, p.top);
15653         return this;
15654     },
15655
15656     // private
15657     onRender : function(ct, position){
15658         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15659         if(this.resizeEl){
15660             this.resizeEl = Roo.get(this.resizeEl);
15661         }
15662         if(this.positionEl){
15663             this.positionEl = Roo.get(this.positionEl);
15664         }
15665     },
15666
15667     // private
15668     afterRender : function(){
15669         Roo.BoxComponent.superclass.afterRender.call(this);
15670         this.boxReady = true;
15671         this.setSize(this.width, this.height);
15672         if(this.x || this.y){
15673             this.setPosition(this.x, this.y);
15674         }
15675         if(this.pageX || this.pageY){
15676             this.setPagePosition(this.pageX, this.pageY);
15677         }
15678     },
15679
15680     /**
15681      * Force the component's size to recalculate based on the underlying element's current height and width.
15682      * @returns {Roo.BoxComponent} this
15683      */
15684     syncSize : function(){
15685         delete this.lastSize;
15686         this.setSize(this.el.getWidth(), this.el.getHeight());
15687         return this;
15688     },
15689
15690     /**
15691      * Called after the component is resized, this method is empty by default but can be implemented by any
15692      * subclass that needs to perform custom logic after a resize occurs.
15693      * @param {Number} adjWidth The box-adjusted width that was set
15694      * @param {Number} adjHeight The box-adjusted height that was set
15695      * @param {Number} rawWidth The width that was originally specified
15696      * @param {Number} rawHeight The height that was originally specified
15697      */
15698     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15699
15700     },
15701
15702     /**
15703      * Called after the component is moved, this method is empty by default but can be implemented by any
15704      * subclass that needs to perform custom logic after a move occurs.
15705      * @param {Number} x The new x position
15706      * @param {Number} y The new y position
15707      */
15708     onPosition : function(x, y){
15709
15710     },
15711
15712     // private
15713     adjustSize : function(w, h){
15714         if(this.autoWidth){
15715             w = 'auto';
15716         }
15717         if(this.autoHeight){
15718             h = 'auto';
15719         }
15720         return {width : w, height: h};
15721     },
15722
15723     // private
15724     adjustPosition : function(x, y){
15725         return {x : x, y: y};
15726     }
15727 });/*
15728  * Original code for Roojs - LGPL
15729  * <script type="text/javascript">
15730  */
15731  
15732 /**
15733  * @class Roo.XComponent
15734  * A delayed Element creator...
15735  * Or a way to group chunks of interface together.
15736  * 
15737  * Mypart.xyx = new Roo.XComponent({
15738
15739     parent : 'Mypart.xyz', // empty == document.element.!!
15740     order : '001',
15741     name : 'xxxx'
15742     region : 'xxxx'
15743     disabled : function() {} 
15744      
15745     tree : function() { // return an tree of xtype declared components
15746         var MODULE = this;
15747         return 
15748         {
15749             xtype : 'NestedLayoutPanel',
15750             // technicall
15751         }
15752      ]
15753  *})
15754  *
15755  *
15756  * It can be used to build a big heiracy, with parent etc.
15757  * or you can just use this to render a single compoent to a dom element
15758  * MYPART.render(Roo.Element | String(id) | dom_element )
15759  * 
15760  * @extends Roo.util.Observable
15761  * @constructor
15762  * @param cfg {Object} configuration of component
15763  * 
15764  */
15765 Roo.XComponent = function(cfg) {
15766     Roo.apply(this, cfg);
15767     this.addEvents({ 
15768         /**
15769              * @event built
15770              * Fires when this the componnt is built
15771              * @param {Roo.XComponent} c the component
15772              */
15773         'built' : true
15774         
15775     });
15776     this.region = this.region || 'center'; // default..
15777     Roo.XComponent.register(this);
15778     this.modules = false;
15779     this.el = false; // where the layout goes..
15780     
15781     
15782 }
15783 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15784     /**
15785      * @property el
15786      * The created element (with Roo.factory())
15787      * @type {Roo.Layout}
15788      */
15789     el  : false,
15790     
15791     /**
15792      * @property el
15793      * for BC  - use el in new code
15794      * @type {Roo.Layout}
15795      */
15796     panel : false,
15797     
15798     /**
15799      * @property layout
15800      * for BC  - use el in new code
15801      * @type {Roo.Layout}
15802      */
15803     layout : false,
15804     
15805      /**
15806      * @cfg {Function|boolean} disabled
15807      * If this module is disabled by some rule, return true from the funtion
15808      */
15809     disabled : false,
15810     
15811     /**
15812      * @cfg {String} parent 
15813      * Name of parent element which it get xtype added to..
15814      */
15815     parent: false,
15816     
15817     /**
15818      * @cfg {String} order
15819      * Used to set the order in which elements are created (usefull for multiple tabs)
15820      */
15821     
15822     order : false,
15823     /**
15824      * @cfg {String} name
15825      * String to display while loading.
15826      */
15827     name : false,
15828     /**
15829      * @cfg {String} region
15830      * Region to render component to (defaults to center)
15831      */
15832     region : 'center',
15833     
15834     /**
15835      * @cfg {Array} items
15836      * A single item array - the first element is the root of the tree..
15837      * It's done this way to stay compatible with the Xtype system...
15838      */
15839     items : false,
15840     
15841     /**
15842      * @property _tree
15843      * The method that retuns the tree of parts that make up this compoennt 
15844      * @type {function}
15845      */
15846     _tree  : false,
15847     
15848      /**
15849      * render
15850      * render element to dom or tree
15851      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15852      */
15853     
15854     render : function(el)
15855     {
15856         
15857         el = el || false;
15858         var hp = this.parent ? 1 : 0;
15859         
15860         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15861             // if parent is a '#.....' string, then let's use that..
15862             var ename = this.parent.substr(1)
15863             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15864             el = Roo.get(ename);
15865             if (!el && !this.parent) {
15866                 Roo.log("Warning - element can not be found :#" + ename );
15867                 return;
15868             }
15869         }
15870         var tree = this._tree ? this._tree() : this.tree();
15871
15872         
15873         if (!this.parent && typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) {
15874             //el = Roo.get(document.body);
15875             this.parent = { el : true };
15876         }
15877             
15878             
15879         
15880         if (!this.parent) {
15881             
15882             Roo.log("no parent - creating one");
15883             
15884             el = el ? Roo.get(el) : false;      
15885             
15886             // it's a top level one..
15887             this.parent =  {
15888                 el : new Roo.BorderLayout(el || document.body, {
15889                 
15890                      center: {
15891                          titlebar: false,
15892                          autoScroll:false,
15893                          closeOnTab: true,
15894                          tabPosition: 'top',
15895                           //resizeTabs: true,
15896                          alwaysShowTabs: el && hp? false :  true,
15897                          hideTabs: el || !hp ? true :  false,
15898                          minTabWidth: 140
15899                      }
15900                  })
15901             }
15902         }
15903         
15904                 if (!this.parent.el) {
15905                         // probably an old style ctor, which has been disabled.
15906                         return;
15907                         
15908                 }
15909                 // The 'tree' method is  '_tree now' 
15910             
15911         tree.region = tree.region || this.region;
15912         
15913         if (this.parent.el === true) {
15914             // bootstrap... - body..
15915             this.parent.el = Roo.factory(tree);
15916         }
15917         
15918         this.el = this.parent.el.addxtype(tree);
15919         this.fireEvent('built', this);
15920         
15921         this.panel = this.el;
15922         this.layout = this.panel.layout;
15923                 this.parentLayout = this.parent.layout  || false;  
15924          
15925     }
15926     
15927 });
15928
15929 Roo.apply(Roo.XComponent, {
15930     /**
15931      * @property  hideProgress
15932      * true to disable the building progress bar.. usefull on single page renders.
15933      * @type Boolean
15934      */
15935     hideProgress : false,
15936     /**
15937      * @property  buildCompleted
15938      * True when the builder has completed building the interface.
15939      * @type Boolean
15940      */
15941     buildCompleted : false,
15942      
15943     /**
15944      * @property  topModule
15945      * the upper most module - uses document.element as it's constructor.
15946      * @type Object
15947      */
15948      
15949     topModule  : false,
15950       
15951     /**
15952      * @property  modules
15953      * array of modules to be created by registration system.
15954      * @type {Array} of Roo.XComponent
15955      */
15956     
15957     modules : [],
15958     /**
15959      * @property  elmodules
15960      * array of modules to be created by which use #ID 
15961      * @type {Array} of Roo.XComponent
15962      */
15963      
15964     elmodules : [],
15965
15966      /**
15967      * @property  build_from_html
15968      * Build elements from html - used by bootstrap HTML stuff 
15969      *    - this is cleared after build is completed
15970      * @type {boolean} true  (default false)
15971      */
15972      
15973     build_from_html : false,
15974
15975     /**
15976      * Register components to be built later.
15977      *
15978      * This solves the following issues
15979      * - Building is not done on page load, but after an authentication process has occured.
15980      * - Interface elements are registered on page load
15981      * - Parent Interface elements may not be loaded before child, so this handles that..
15982      * 
15983      *
15984      * example:
15985      * 
15986      * MyApp.register({
15987           order : '000001',
15988           module : 'Pman.Tab.projectMgr',
15989           region : 'center',
15990           parent : 'Pman.layout',
15991           disabled : false,  // or use a function..
15992         })
15993      
15994      * * @param {Object} details about module
15995      */
15996     register : function(obj) {
15997                 
15998         Roo.XComponent.event.fireEvent('register', obj);
15999         switch(typeof(obj.disabled) ) {
16000                 
16001             case 'undefined':
16002                 break;
16003             
16004             case 'function':
16005                 if ( obj.disabled() ) {
16006                         return;
16007                 }
16008                 break;
16009             
16010             default:
16011                 if (obj.disabled) {
16012                         return;
16013                 }
16014                 break;
16015         }
16016                 
16017         this.modules.push(obj);
16018          
16019     },
16020     /**
16021      * convert a string to an object..
16022      * eg. 'AAA.BBB' -> finds AAA.BBB
16023
16024      */
16025     
16026     toObject : function(str)
16027     {
16028         if (!str || typeof(str) == 'object') {
16029             return str;
16030         }
16031         if (str.substring(0,1) == '#') {
16032             return str;
16033         }
16034
16035         var ar = str.split('.');
16036         var rt, o;
16037         rt = ar.shift();
16038             /** eval:var:o */
16039         try {
16040             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16041         } catch (e) {
16042             throw "Module not found : " + str;
16043         }
16044         
16045         if (o === false) {
16046             throw "Module not found : " + str;
16047         }
16048         Roo.each(ar, function(e) {
16049             if (typeof(o[e]) == 'undefined') {
16050                 throw "Module not found : " + str;
16051             }
16052             o = o[e];
16053         });
16054         
16055         return o;
16056         
16057     },
16058     
16059     
16060     /**
16061      * move modules into their correct place in the tree..
16062      * 
16063      */
16064     preBuild : function ()
16065     {
16066         var _t = this;
16067         Roo.each(this.modules , function (obj)
16068         {
16069             Roo.XComponent.event.fireEvent('beforebuild', obj);
16070             
16071             var opar = obj.parent;
16072             try { 
16073                 obj.parent = this.toObject(opar);
16074             } catch(e) {
16075                 Roo.log("parent:toObject failed: " + e.toString());
16076                 return;
16077             }
16078             
16079             if (!obj.parent) {
16080                 Roo.debug && Roo.log("GOT top level module");
16081                 Roo.debug && Roo.log(obj);
16082                 obj.modules = new Roo.util.MixedCollection(false, 
16083                     function(o) { return o.order + '' }
16084                 );
16085                 this.topModule = obj;
16086                 return;
16087             }
16088                         // parent is a string (usually a dom element name..)
16089             if (typeof(obj.parent) == 'string') {
16090                 this.elmodules.push(obj);
16091                 return;
16092             }
16093             if (obj.parent.constructor != Roo.XComponent) {
16094                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16095             }
16096             if (!obj.parent.modules) {
16097                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16098                     function(o) { return o.order + '' }
16099                 );
16100             }
16101             if (obj.parent.disabled) {
16102                 obj.disabled = true;
16103             }
16104             obj.parent.modules.add(obj);
16105         }, this);
16106     },
16107     
16108      /**
16109      * make a list of modules to build.
16110      * @return {Array} list of modules. 
16111      */ 
16112     
16113     buildOrder : function()
16114     {
16115         var _this = this;
16116         var cmp = function(a,b) {   
16117             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16118         };
16119         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16120             throw "No top level modules to build";
16121         }
16122         
16123         // make a flat list in order of modules to build.
16124         var mods = this.topModule ? [ this.topModule ] : [];
16125                 
16126         
16127         // elmodules (is a list of DOM based modules )
16128         Roo.each(this.elmodules, function(e) {
16129             mods.push(e);
16130             if (!this.topModule &&
16131                 typeof(e.parent) == 'string' &&
16132                 e.parent.substring(0,1) == '#' &&
16133                 Roo.get(e.parent.substr(1))
16134                ) {
16135                 
16136                 _this.topModule = e;
16137             }
16138             
16139         });
16140
16141         
16142         // add modules to their parents..
16143         var addMod = function(m) {
16144             Roo.debug && Roo.log("build Order: add: " + m.name);
16145                 
16146             mods.push(m);
16147             if (m.modules && !m.disabled) {
16148                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16149                 m.modules.keySort('ASC',  cmp );
16150                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16151     
16152                 m.modules.each(addMod);
16153             } else {
16154                 Roo.debug && Roo.log("build Order: no child modules");
16155             }
16156             // not sure if this is used any more..
16157             if (m.finalize) {
16158                 m.finalize.name = m.name + " (clean up) ";
16159                 mods.push(m.finalize);
16160             }
16161             
16162         }
16163         if (this.topModule && this.topModule.modules) { 
16164             this.topModule.modules.keySort('ASC',  cmp );
16165             this.topModule.modules.each(addMod);
16166         } 
16167         return mods;
16168     },
16169     
16170      /**
16171      * Build the registered modules.
16172      * @param {Object} parent element.
16173      * @param {Function} optional method to call after module has been added.
16174      * 
16175      */ 
16176    
16177     build : function(opts) 
16178     {
16179         
16180         if (typeof(opts) != 'undefined') {
16181             Roo.apply(this,opts);
16182         }
16183         
16184         this.preBuild();
16185         var mods = this.buildOrder();
16186       
16187         //this.allmods = mods;
16188         //Roo.debug && Roo.log(mods);
16189         //return;
16190         if (!mods.length) { // should not happen
16191             throw "NO modules!!!";
16192         }
16193         
16194         
16195         var msg = "Building Interface...";
16196         // flash it up as modal - so we store the mask!?
16197         if (!this.hideProgress && Roo.MessageBox) {
16198             Roo.MessageBox.show({ title: 'loading' });
16199             Roo.MessageBox.show({
16200                title: "Please wait...",
16201                msg: msg,
16202                width:450,
16203                progress:true,
16204                closable:false,
16205                modal: false
16206               
16207             });
16208         }
16209         var total = mods.length;
16210         
16211         var _this = this;
16212         var progressRun = function() {
16213             if (!mods.length) {
16214                 Roo.debug && Roo.log('hide?');
16215                 if (!this.hideProgress && Roo.MessageBox) {
16216                     Roo.MessageBox.hide();
16217                 }
16218                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16219                 
16220                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16221                 
16222                 // THE END...
16223                 return false;   
16224             }
16225             
16226             var m = mods.shift();
16227             
16228             
16229             Roo.debug && Roo.log(m);
16230             // not sure if this is supported any more.. - modules that are are just function
16231             if (typeof(m) == 'function') { 
16232                 m.call(this);
16233                 return progressRun.defer(10, _this);
16234             } 
16235             
16236             
16237             msg = "Building Interface " + (total  - mods.length) + 
16238                     " of " + total + 
16239                     (m.name ? (' - ' + m.name) : '');
16240                         Roo.debug && Roo.log(msg);
16241             if (!this.hideProgress &&  Roo.MessageBox) { 
16242                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16243             }
16244             
16245          
16246             // is the module disabled?
16247             var disabled = (typeof(m.disabled) == 'function') ?
16248                 m.disabled.call(m.module.disabled) : m.disabled;    
16249             
16250             
16251             if (disabled) {
16252                 return progressRun(); // we do not update the display!
16253             }
16254             
16255             // now build 
16256             
16257                         
16258                         
16259             m.render();
16260             // it's 10 on top level, and 1 on others??? why...
16261             return progressRun.defer(10, _this);
16262              
16263         }
16264         progressRun.defer(1, _this);
16265      
16266         
16267         
16268     },
16269         
16270         
16271         /**
16272          * Event Object.
16273          *
16274          *
16275          */
16276         event: false, 
16277     /**
16278          * wrapper for event.on - aliased later..  
16279          * Typically use to register a event handler for register:
16280          *
16281          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16282          *
16283          */
16284     on : false
16285    
16286     
16287     
16288 });
16289
16290 Roo.XComponent.event = new Roo.util.Observable({
16291                 events : { 
16292                         /**
16293                          * @event register
16294                          * Fires when an Component is registered,
16295                          * set the disable property on the Component to stop registration.
16296                          * @param {Roo.XComponent} c the component being registerd.
16297                          * 
16298                          */
16299                         'register' : true,
16300             /**
16301                          * @event beforebuild
16302                          * Fires before each Component is built
16303                          * can be used to apply permissions.
16304                          * @param {Roo.XComponent} c the component being registerd.
16305                          * 
16306                          */
16307                         'beforebuild' : true,
16308                         /**
16309                          * @event buildcomplete
16310                          * Fires on the top level element when all elements have been built
16311                          * @param {Roo.XComponent} the top level component.
16312                          */
16313                         'buildcomplete' : true
16314                         
16315                 }
16316 });
16317
16318 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16319  /*
16320  * Based on:
16321  * Ext JS Library 1.1.1
16322  * Copyright(c) 2006-2007, Ext JS, LLC.
16323  *
16324  * Originally Released Under LGPL - original licence link has changed is not relivant.
16325  *
16326  * Fork - LGPL
16327  * <script type="text/javascript">
16328  */
16329
16330
16331
16332 /*
16333  * These classes are derivatives of the similarly named classes in the YUI Library.
16334  * The original license:
16335  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16336  * Code licensed under the BSD License:
16337  * http://developer.yahoo.net/yui/license.txt
16338  */
16339
16340 (function() {
16341
16342 var Event=Roo.EventManager;
16343 var Dom=Roo.lib.Dom;
16344
16345 /**
16346  * @class Roo.dd.DragDrop
16347  * @extends Roo.util.Observable
16348  * Defines the interface and base operation of items that that can be
16349  * dragged or can be drop targets.  It was designed to be extended, overriding
16350  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16351  * Up to three html elements can be associated with a DragDrop instance:
16352  * <ul>
16353  * <li>linked element: the element that is passed into the constructor.
16354  * This is the element which defines the boundaries for interaction with
16355  * other DragDrop objects.</li>
16356  * <li>handle element(s): The drag operation only occurs if the element that
16357  * was clicked matches a handle element.  By default this is the linked
16358  * element, but there are times that you will want only a portion of the
16359  * linked element to initiate the drag operation, and the setHandleElId()
16360  * method provides a way to define this.</li>
16361  * <li>drag element: this represents the element that would be moved along
16362  * with the cursor during a drag operation.  By default, this is the linked
16363  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16364  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16365  * </li>
16366  * </ul>
16367  * This class should not be instantiated until the onload event to ensure that
16368  * the associated elements are available.
16369  * The following would define a DragDrop obj that would interact with any
16370  * other DragDrop obj in the "group1" group:
16371  * <pre>
16372  *  dd = new Roo.dd.DragDrop("div1", "group1");
16373  * </pre>
16374  * Since none of the event handlers have been implemented, nothing would
16375  * actually happen if you were to run the code above.  Normally you would
16376  * override this class or one of the default implementations, but you can
16377  * also override the methods you want on an instance of the class...
16378  * <pre>
16379  *  dd.onDragDrop = function(e, id) {
16380  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16381  *  }
16382  * </pre>
16383  * @constructor
16384  * @param {String} id of the element that is linked to this instance
16385  * @param {String} sGroup the group of related DragDrop objects
16386  * @param {object} config an object containing configurable attributes
16387  *                Valid properties for DragDrop:
16388  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16389  */
16390 Roo.dd.DragDrop = function(id, sGroup, config) {
16391     if (id) {
16392         this.init(id, sGroup, config);
16393     }
16394     
16395 };
16396
16397 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16398
16399     /**
16400      * The id of the element associated with this object.  This is what we
16401      * refer to as the "linked element" because the size and position of
16402      * this element is used to determine when the drag and drop objects have
16403      * interacted.
16404      * @property id
16405      * @type String
16406      */
16407     id: null,
16408
16409     /**
16410      * Configuration attributes passed into the constructor
16411      * @property config
16412      * @type object
16413      */
16414     config: null,
16415
16416     /**
16417      * The id of the element that will be dragged.  By default this is same
16418      * as the linked element , but could be changed to another element. Ex:
16419      * Roo.dd.DDProxy
16420      * @property dragElId
16421      * @type String
16422      * @private
16423      */
16424     dragElId: null,
16425
16426     /**
16427      * the id of the element that initiates the drag operation.  By default
16428      * this is the linked element, but could be changed to be a child of this
16429      * element.  This lets us do things like only starting the drag when the
16430      * header element within the linked html element is clicked.
16431      * @property handleElId
16432      * @type String
16433      * @private
16434      */
16435     handleElId: null,
16436
16437     /**
16438      * An associative array of HTML tags that will be ignored if clicked.
16439      * @property invalidHandleTypes
16440      * @type {string: string}
16441      */
16442     invalidHandleTypes: null,
16443
16444     /**
16445      * An associative array of ids for elements that will be ignored if clicked
16446      * @property invalidHandleIds
16447      * @type {string: string}
16448      */
16449     invalidHandleIds: null,
16450
16451     /**
16452      * An indexted array of css class names for elements that will be ignored
16453      * if clicked.
16454      * @property invalidHandleClasses
16455      * @type string[]
16456      */
16457     invalidHandleClasses: null,
16458
16459     /**
16460      * The linked element's absolute X position at the time the drag was
16461      * started
16462      * @property startPageX
16463      * @type int
16464      * @private
16465      */
16466     startPageX: 0,
16467
16468     /**
16469      * The linked element's absolute X position at the time the drag was
16470      * started
16471      * @property startPageY
16472      * @type int
16473      * @private
16474      */
16475     startPageY: 0,
16476
16477     /**
16478      * The group defines a logical collection of DragDrop objects that are
16479      * related.  Instances only get events when interacting with other
16480      * DragDrop object in the same group.  This lets us define multiple
16481      * groups using a single DragDrop subclass if we want.
16482      * @property groups
16483      * @type {string: string}
16484      */
16485     groups: null,
16486
16487     /**
16488      * Individual drag/drop instances can be locked.  This will prevent
16489      * onmousedown start drag.
16490      * @property locked
16491      * @type boolean
16492      * @private
16493      */
16494     locked: false,
16495
16496     /**
16497      * Lock this instance
16498      * @method lock
16499      */
16500     lock: function() { this.locked = true; },
16501
16502     /**
16503      * Unlock this instace
16504      * @method unlock
16505      */
16506     unlock: function() { this.locked = false; },
16507
16508     /**
16509      * By default, all insances can be a drop target.  This can be disabled by
16510      * setting isTarget to false.
16511      * @method isTarget
16512      * @type boolean
16513      */
16514     isTarget: true,
16515
16516     /**
16517      * The padding configured for this drag and drop object for calculating
16518      * the drop zone intersection with this object.
16519      * @method padding
16520      * @type int[]
16521      */
16522     padding: null,
16523
16524     /**
16525      * Cached reference to the linked element
16526      * @property _domRef
16527      * @private
16528      */
16529     _domRef: null,
16530
16531     /**
16532      * Internal typeof flag
16533      * @property __ygDragDrop
16534      * @private
16535      */
16536     __ygDragDrop: true,
16537
16538     /**
16539      * Set to true when horizontal contraints are applied
16540      * @property constrainX
16541      * @type boolean
16542      * @private
16543      */
16544     constrainX: false,
16545
16546     /**
16547      * Set to true when vertical contraints are applied
16548      * @property constrainY
16549      * @type boolean
16550      * @private
16551      */
16552     constrainY: false,
16553
16554     /**
16555      * The left constraint
16556      * @property minX
16557      * @type int
16558      * @private
16559      */
16560     minX: 0,
16561
16562     /**
16563      * The right constraint
16564      * @property maxX
16565      * @type int
16566      * @private
16567      */
16568     maxX: 0,
16569
16570     /**
16571      * The up constraint
16572      * @property minY
16573      * @type int
16574      * @type int
16575      * @private
16576      */
16577     minY: 0,
16578
16579     /**
16580      * The down constraint
16581      * @property maxY
16582      * @type int
16583      * @private
16584      */
16585     maxY: 0,
16586
16587     /**
16588      * Maintain offsets when we resetconstraints.  Set to true when you want
16589      * the position of the element relative to its parent to stay the same
16590      * when the page changes
16591      *
16592      * @property maintainOffset
16593      * @type boolean
16594      */
16595     maintainOffset: false,
16596
16597     /**
16598      * Array of pixel locations the element will snap to if we specified a
16599      * horizontal graduation/interval.  This array is generated automatically
16600      * when you define a tick interval.
16601      * @property xTicks
16602      * @type int[]
16603      */
16604     xTicks: null,
16605
16606     /**
16607      * Array of pixel locations the element will snap to if we specified a
16608      * vertical graduation/interval.  This array is generated automatically
16609      * when you define a tick interval.
16610      * @property yTicks
16611      * @type int[]
16612      */
16613     yTicks: null,
16614
16615     /**
16616      * By default the drag and drop instance will only respond to the primary
16617      * button click (left button for a right-handed mouse).  Set to true to
16618      * allow drag and drop to start with any mouse click that is propogated
16619      * by the browser
16620      * @property primaryButtonOnly
16621      * @type boolean
16622      */
16623     primaryButtonOnly: true,
16624
16625     /**
16626      * The availabe property is false until the linked dom element is accessible.
16627      * @property available
16628      * @type boolean
16629      */
16630     available: false,
16631
16632     /**
16633      * By default, drags can only be initiated if the mousedown occurs in the
16634      * region the linked element is.  This is done in part to work around a
16635      * bug in some browsers that mis-report the mousedown if the previous
16636      * mouseup happened outside of the window.  This property is set to true
16637      * if outer handles are defined.
16638      *
16639      * @property hasOuterHandles
16640      * @type boolean
16641      * @default false
16642      */
16643     hasOuterHandles: false,
16644
16645     /**
16646      * Code that executes immediately before the startDrag event
16647      * @method b4StartDrag
16648      * @private
16649      */
16650     b4StartDrag: function(x, y) { },
16651
16652     /**
16653      * Abstract method called after a drag/drop object is clicked
16654      * and the drag or mousedown time thresholds have beeen met.
16655      * @method startDrag
16656      * @param {int} X click location
16657      * @param {int} Y click location
16658      */
16659     startDrag: function(x, y) { /* override this */ },
16660
16661     /**
16662      * Code that executes immediately before the onDrag event
16663      * @method b4Drag
16664      * @private
16665      */
16666     b4Drag: function(e) { },
16667
16668     /**
16669      * Abstract method called during the onMouseMove event while dragging an
16670      * object.
16671      * @method onDrag
16672      * @param {Event} e the mousemove event
16673      */
16674     onDrag: function(e) { /* override this */ },
16675
16676     /**
16677      * Abstract method called when this element fist begins hovering over
16678      * another DragDrop obj
16679      * @method onDragEnter
16680      * @param {Event} e the mousemove event
16681      * @param {String|DragDrop[]} id In POINT mode, the element
16682      * id this is hovering over.  In INTERSECT mode, an array of one or more
16683      * dragdrop items being hovered over.
16684      */
16685     onDragEnter: function(e, id) { /* override this */ },
16686
16687     /**
16688      * Code that executes immediately before the onDragOver event
16689      * @method b4DragOver
16690      * @private
16691      */
16692     b4DragOver: function(e) { },
16693
16694     /**
16695      * Abstract method called when this element is hovering over another
16696      * DragDrop obj
16697      * @method onDragOver
16698      * @param {Event} e the mousemove event
16699      * @param {String|DragDrop[]} id In POINT mode, the element
16700      * id this is hovering over.  In INTERSECT mode, an array of dd items
16701      * being hovered over.
16702      */
16703     onDragOver: function(e, id) { /* override this */ },
16704
16705     /**
16706      * Code that executes immediately before the onDragOut event
16707      * @method b4DragOut
16708      * @private
16709      */
16710     b4DragOut: function(e) { },
16711
16712     /**
16713      * Abstract method called when we are no longer hovering over an element
16714      * @method onDragOut
16715      * @param {Event} e the mousemove event
16716      * @param {String|DragDrop[]} id In POINT mode, the element
16717      * id this was hovering over.  In INTERSECT mode, an array of dd items
16718      * that the mouse is no longer over.
16719      */
16720     onDragOut: function(e, id) { /* override this */ },
16721
16722     /**
16723      * Code that executes immediately before the onDragDrop event
16724      * @method b4DragDrop
16725      * @private
16726      */
16727     b4DragDrop: function(e) { },
16728
16729     /**
16730      * Abstract method called when this item is dropped on another DragDrop
16731      * obj
16732      * @method onDragDrop
16733      * @param {Event} e the mouseup event
16734      * @param {String|DragDrop[]} id In POINT mode, the element
16735      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16736      * was dropped on.
16737      */
16738     onDragDrop: function(e, id) { /* override this */ },
16739
16740     /**
16741      * Abstract method called when this item is dropped on an area with no
16742      * drop target
16743      * @method onInvalidDrop
16744      * @param {Event} e the mouseup event
16745      */
16746     onInvalidDrop: function(e) { /* override this */ },
16747
16748     /**
16749      * Code that executes immediately before the endDrag event
16750      * @method b4EndDrag
16751      * @private
16752      */
16753     b4EndDrag: function(e) { },
16754
16755     /**
16756      * Fired when we are done dragging the object
16757      * @method endDrag
16758      * @param {Event} e the mouseup event
16759      */
16760     endDrag: function(e) { /* override this */ },
16761
16762     /**
16763      * Code executed immediately before the onMouseDown event
16764      * @method b4MouseDown
16765      * @param {Event} e the mousedown event
16766      * @private
16767      */
16768     b4MouseDown: function(e) {  },
16769
16770     /**
16771      * Event handler that fires when a drag/drop obj gets a mousedown
16772      * @method onMouseDown
16773      * @param {Event} e the mousedown event
16774      */
16775     onMouseDown: function(e) { /* override this */ },
16776
16777     /**
16778      * Event handler that fires when a drag/drop obj gets a mouseup
16779      * @method onMouseUp
16780      * @param {Event} e the mouseup event
16781      */
16782     onMouseUp: function(e) { /* override this */ },
16783
16784     /**
16785      * Override the onAvailable method to do what is needed after the initial
16786      * position was determined.
16787      * @method onAvailable
16788      */
16789     onAvailable: function () {
16790     },
16791
16792     /*
16793      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16794      * @type Object
16795      */
16796     defaultPadding : {left:0, right:0, top:0, bottom:0},
16797
16798     /*
16799      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16800  *
16801  * Usage:
16802  <pre><code>
16803  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16804                 { dragElId: "existingProxyDiv" });
16805  dd.startDrag = function(){
16806      this.constrainTo("parent-id");
16807  };
16808  </code></pre>
16809  * Or you can initalize it using the {@link Roo.Element} object:
16810  <pre><code>
16811  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16812      startDrag : function(){
16813          this.constrainTo("parent-id");
16814      }
16815  });
16816  </code></pre>
16817      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16818      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16819      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16820      * an object containing the sides to pad. For example: {right:10, bottom:10}
16821      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16822      */
16823     constrainTo : function(constrainTo, pad, inContent){
16824         if(typeof pad == "number"){
16825             pad = {left: pad, right:pad, top:pad, bottom:pad};
16826         }
16827         pad = pad || this.defaultPadding;
16828         var b = Roo.get(this.getEl()).getBox();
16829         var ce = Roo.get(constrainTo);
16830         var s = ce.getScroll();
16831         var c, cd = ce.dom;
16832         if(cd == document.body){
16833             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16834         }else{
16835             xy = ce.getXY();
16836             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16837         }
16838
16839
16840         var topSpace = b.y - c.y;
16841         var leftSpace = b.x - c.x;
16842
16843         this.resetConstraints();
16844         this.setXConstraint(leftSpace - (pad.left||0), // left
16845                 c.width - leftSpace - b.width - (pad.right||0) //right
16846         );
16847         this.setYConstraint(topSpace - (pad.top||0), //top
16848                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16849         );
16850     },
16851
16852     /**
16853      * Returns a reference to the linked element
16854      * @method getEl
16855      * @return {HTMLElement} the html element
16856      */
16857     getEl: function() {
16858         if (!this._domRef) {
16859             this._domRef = Roo.getDom(this.id);
16860         }
16861
16862         return this._domRef;
16863     },
16864
16865     /**
16866      * Returns a reference to the actual element to drag.  By default this is
16867      * the same as the html element, but it can be assigned to another
16868      * element. An example of this can be found in Roo.dd.DDProxy
16869      * @method getDragEl
16870      * @return {HTMLElement} the html element
16871      */
16872     getDragEl: function() {
16873         return Roo.getDom(this.dragElId);
16874     },
16875
16876     /**
16877      * Sets up the DragDrop object.  Must be called in the constructor of any
16878      * Roo.dd.DragDrop subclass
16879      * @method init
16880      * @param id the id of the linked element
16881      * @param {String} sGroup the group of related items
16882      * @param {object} config configuration attributes
16883      */
16884     init: function(id, sGroup, config) {
16885         this.initTarget(id, sGroup, config);
16886         if (!Roo.isTouch) {
16887             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16888         }
16889         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16890         // Event.on(this.id, "selectstart", Event.preventDefault);
16891     },
16892
16893     /**
16894      * Initializes Targeting functionality only... the object does not
16895      * get a mousedown handler.
16896      * @method initTarget
16897      * @param id the id of the linked element
16898      * @param {String} sGroup the group of related items
16899      * @param {object} config configuration attributes
16900      */
16901     initTarget: function(id, sGroup, config) {
16902
16903         // configuration attributes
16904         this.config = config || {};
16905
16906         // create a local reference to the drag and drop manager
16907         this.DDM = Roo.dd.DDM;
16908         // initialize the groups array
16909         this.groups = {};
16910
16911         // assume that we have an element reference instead of an id if the
16912         // parameter is not a string
16913         if (typeof id !== "string") {
16914             id = Roo.id(id);
16915         }
16916
16917         // set the id
16918         this.id = id;
16919
16920         // add to an interaction group
16921         this.addToGroup((sGroup) ? sGroup : "default");
16922
16923         // We don't want to register this as the handle with the manager
16924         // so we just set the id rather than calling the setter.
16925         this.handleElId = id;
16926
16927         // the linked element is the element that gets dragged by default
16928         this.setDragElId(id);
16929
16930         // by default, clicked anchors will not start drag operations.
16931         this.invalidHandleTypes = { A: "A" };
16932         this.invalidHandleIds = {};
16933         this.invalidHandleClasses = [];
16934
16935         this.applyConfig();
16936
16937         this.handleOnAvailable();
16938     },
16939
16940     /**
16941      * Applies the configuration parameters that were passed into the constructor.
16942      * This is supposed to happen at each level through the inheritance chain.  So
16943      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16944      * DragDrop in order to get all of the parameters that are available in
16945      * each object.
16946      * @method applyConfig
16947      */
16948     applyConfig: function() {
16949
16950         // configurable properties:
16951         //    padding, isTarget, maintainOffset, primaryButtonOnly
16952         this.padding           = this.config.padding || [0, 0, 0, 0];
16953         this.isTarget          = (this.config.isTarget !== false);
16954         this.maintainOffset    = (this.config.maintainOffset);
16955         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16956
16957     },
16958
16959     /**
16960      * Executed when the linked element is available
16961      * @method handleOnAvailable
16962      * @private
16963      */
16964     handleOnAvailable: function() {
16965         this.available = true;
16966         this.resetConstraints();
16967         this.onAvailable();
16968     },
16969
16970      /**
16971      * Configures the padding for the target zone in px.  Effectively expands
16972      * (or reduces) the virtual object size for targeting calculations.
16973      * Supports css-style shorthand; if only one parameter is passed, all sides
16974      * will have that padding, and if only two are passed, the top and bottom
16975      * will have the first param, the left and right the second.
16976      * @method setPadding
16977      * @param {int} iTop    Top pad
16978      * @param {int} iRight  Right pad
16979      * @param {int} iBot    Bot pad
16980      * @param {int} iLeft   Left pad
16981      */
16982     setPadding: function(iTop, iRight, iBot, iLeft) {
16983         // this.padding = [iLeft, iRight, iTop, iBot];
16984         if (!iRight && 0 !== iRight) {
16985             this.padding = [iTop, iTop, iTop, iTop];
16986         } else if (!iBot && 0 !== iBot) {
16987             this.padding = [iTop, iRight, iTop, iRight];
16988         } else {
16989             this.padding = [iTop, iRight, iBot, iLeft];
16990         }
16991     },
16992
16993     /**
16994      * Stores the initial placement of the linked element.
16995      * @method setInitialPosition
16996      * @param {int} diffX   the X offset, default 0
16997      * @param {int} diffY   the Y offset, default 0
16998      */
16999     setInitPosition: function(diffX, diffY) {
17000         var el = this.getEl();
17001
17002         if (!this.DDM.verifyEl(el)) {
17003             return;
17004         }
17005
17006         var dx = diffX || 0;
17007         var dy = diffY || 0;
17008
17009         var p = Dom.getXY( el );
17010
17011         this.initPageX = p[0] - dx;
17012         this.initPageY = p[1] - dy;
17013
17014         this.lastPageX = p[0];
17015         this.lastPageY = p[1];
17016
17017
17018         this.setStartPosition(p);
17019     },
17020
17021     /**
17022      * Sets the start position of the element.  This is set when the obj
17023      * is initialized, the reset when a drag is started.
17024      * @method setStartPosition
17025      * @param pos current position (from previous lookup)
17026      * @private
17027      */
17028     setStartPosition: function(pos) {
17029         var p = pos || Dom.getXY( this.getEl() );
17030         this.deltaSetXY = null;
17031
17032         this.startPageX = p[0];
17033         this.startPageY = p[1];
17034     },
17035
17036     /**
17037      * Add this instance to a group of related drag/drop objects.  All
17038      * instances belong to at least one group, and can belong to as many
17039      * groups as needed.
17040      * @method addToGroup
17041      * @param sGroup {string} the name of the group
17042      */
17043     addToGroup: function(sGroup) {
17044         this.groups[sGroup] = true;
17045         this.DDM.regDragDrop(this, sGroup);
17046     },
17047
17048     /**
17049      * Remove's this instance from the supplied interaction group
17050      * @method removeFromGroup
17051      * @param {string}  sGroup  The group to drop
17052      */
17053     removeFromGroup: function(sGroup) {
17054         if (this.groups[sGroup]) {
17055             delete this.groups[sGroup];
17056         }
17057
17058         this.DDM.removeDDFromGroup(this, sGroup);
17059     },
17060
17061     /**
17062      * Allows you to specify that an element other than the linked element
17063      * will be moved with the cursor during a drag
17064      * @method setDragElId
17065      * @param id {string} the id of the element that will be used to initiate the drag
17066      */
17067     setDragElId: function(id) {
17068         this.dragElId = id;
17069     },
17070
17071     /**
17072      * Allows you to specify a child of the linked element that should be
17073      * used to initiate the drag operation.  An example of this would be if
17074      * you have a content div with text and links.  Clicking anywhere in the
17075      * content area would normally start the drag operation.  Use this method
17076      * to specify that an element inside of the content div is the element
17077      * that starts the drag operation.
17078      * @method setHandleElId
17079      * @param id {string} the id of the element that will be used to
17080      * initiate the drag.
17081      */
17082     setHandleElId: function(id) {
17083         if (typeof id !== "string") {
17084             id = Roo.id(id);
17085         }
17086         this.handleElId = id;
17087         this.DDM.regHandle(this.id, id);
17088     },
17089
17090     /**
17091      * Allows you to set an element outside of the linked element as a drag
17092      * handle
17093      * @method setOuterHandleElId
17094      * @param id the id of the element that will be used to initiate the drag
17095      */
17096     setOuterHandleElId: function(id) {
17097         if (typeof id !== "string") {
17098             id = Roo.id(id);
17099         }
17100         Event.on(id, "mousedown",
17101                 this.handleMouseDown, this);
17102         this.setHandleElId(id);
17103
17104         this.hasOuterHandles = true;
17105     },
17106
17107     /**
17108      * Remove all drag and drop hooks for this element
17109      * @method unreg
17110      */
17111     unreg: function() {
17112         Event.un(this.id, "mousedown",
17113                 this.handleMouseDown);
17114         Event.un(this.id, "touchstart",
17115                 this.handleMouseDown);
17116         this._domRef = null;
17117         this.DDM._remove(this);
17118     },
17119
17120     destroy : function(){
17121         this.unreg();
17122     },
17123
17124     /**
17125      * Returns true if this instance is locked, or the drag drop mgr is locked
17126      * (meaning that all drag/drop is disabled on the page.)
17127      * @method isLocked
17128      * @return {boolean} true if this obj or all drag/drop is locked, else
17129      * false
17130      */
17131     isLocked: function() {
17132         return (this.DDM.isLocked() || this.locked);
17133     },
17134
17135     /**
17136      * Fired when this object is clicked
17137      * @method handleMouseDown
17138      * @param {Event} e
17139      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17140      * @private
17141      */
17142     handleMouseDown: function(e, oDD){
17143      
17144         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17145             //Roo.log('not touch/ button !=0');
17146             return;
17147         }
17148         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17149             return; // double touch..
17150         }
17151         
17152
17153         if (this.isLocked()) {
17154             //Roo.log('locked');
17155             return;
17156         }
17157
17158         this.DDM.refreshCache(this.groups);
17159 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17160         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17161         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17162             //Roo.log('no outer handes or not over target');
17163                 // do nothing.
17164         } else {
17165 //            Roo.log('check validator');
17166             if (this.clickValidator(e)) {
17167 //                Roo.log('validate success');
17168                 // set the initial element position
17169                 this.setStartPosition();
17170
17171
17172                 this.b4MouseDown(e);
17173                 this.onMouseDown(e);
17174
17175                 this.DDM.handleMouseDown(e, this);
17176
17177                 this.DDM.stopEvent(e);
17178             } else {
17179
17180
17181             }
17182         }
17183     },
17184
17185     clickValidator: function(e) {
17186         var target = e.getTarget();
17187         return ( this.isValidHandleChild(target) &&
17188                     (this.id == this.handleElId ||
17189                         this.DDM.handleWasClicked(target, this.id)) );
17190     },
17191
17192     /**
17193      * Allows you to specify a tag name that should not start a drag operation
17194      * when clicked.  This is designed to facilitate embedding links within a
17195      * drag handle that do something other than start the drag.
17196      * @method addInvalidHandleType
17197      * @param {string} tagName the type of element to exclude
17198      */
17199     addInvalidHandleType: function(tagName) {
17200         var type = tagName.toUpperCase();
17201         this.invalidHandleTypes[type] = type;
17202     },
17203
17204     /**
17205      * Lets you to specify an element id for a child of a drag handle
17206      * that should not initiate a drag
17207      * @method addInvalidHandleId
17208      * @param {string} id the element id of the element you wish to ignore
17209      */
17210     addInvalidHandleId: function(id) {
17211         if (typeof id !== "string") {
17212             id = Roo.id(id);
17213         }
17214         this.invalidHandleIds[id] = id;
17215     },
17216
17217     /**
17218      * Lets you specify a css class of elements that will not initiate a drag
17219      * @method addInvalidHandleClass
17220      * @param {string} cssClass the class of the elements you wish to ignore
17221      */
17222     addInvalidHandleClass: function(cssClass) {
17223         this.invalidHandleClasses.push(cssClass);
17224     },
17225
17226     /**
17227      * Unsets an excluded tag name set by addInvalidHandleType
17228      * @method removeInvalidHandleType
17229      * @param {string} tagName the type of element to unexclude
17230      */
17231     removeInvalidHandleType: function(tagName) {
17232         var type = tagName.toUpperCase();
17233         // this.invalidHandleTypes[type] = null;
17234         delete this.invalidHandleTypes[type];
17235     },
17236
17237     /**
17238      * Unsets an invalid handle id
17239      * @method removeInvalidHandleId
17240      * @param {string} id the id of the element to re-enable
17241      */
17242     removeInvalidHandleId: function(id) {
17243         if (typeof id !== "string") {
17244             id = Roo.id(id);
17245         }
17246         delete this.invalidHandleIds[id];
17247     },
17248
17249     /**
17250      * Unsets an invalid css class
17251      * @method removeInvalidHandleClass
17252      * @param {string} cssClass the class of the element(s) you wish to
17253      * re-enable
17254      */
17255     removeInvalidHandleClass: function(cssClass) {
17256         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17257             if (this.invalidHandleClasses[i] == cssClass) {
17258                 delete this.invalidHandleClasses[i];
17259             }
17260         }
17261     },
17262
17263     /**
17264      * Checks the tag exclusion list to see if this click should be ignored
17265      * @method isValidHandleChild
17266      * @param {HTMLElement} node the HTMLElement to evaluate
17267      * @return {boolean} true if this is a valid tag type, false if not
17268      */
17269     isValidHandleChild: function(node) {
17270
17271         var valid = true;
17272         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17273         var nodeName;
17274         try {
17275             nodeName = node.nodeName.toUpperCase();
17276         } catch(e) {
17277             nodeName = node.nodeName;
17278         }
17279         valid = valid && !this.invalidHandleTypes[nodeName];
17280         valid = valid && !this.invalidHandleIds[node.id];
17281
17282         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17283             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17284         }
17285
17286
17287         return valid;
17288
17289     },
17290
17291     /**
17292      * Create the array of horizontal tick marks if an interval was specified
17293      * in setXConstraint().
17294      * @method setXTicks
17295      * @private
17296      */
17297     setXTicks: function(iStartX, iTickSize) {
17298         this.xTicks = [];
17299         this.xTickSize = iTickSize;
17300
17301         var tickMap = {};
17302
17303         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17304             if (!tickMap[i]) {
17305                 this.xTicks[this.xTicks.length] = i;
17306                 tickMap[i] = true;
17307             }
17308         }
17309
17310         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17311             if (!tickMap[i]) {
17312                 this.xTicks[this.xTicks.length] = i;
17313                 tickMap[i] = true;
17314             }
17315         }
17316
17317         this.xTicks.sort(this.DDM.numericSort) ;
17318     },
17319
17320     /**
17321      * Create the array of vertical tick marks if an interval was specified in
17322      * setYConstraint().
17323      * @method setYTicks
17324      * @private
17325      */
17326     setYTicks: function(iStartY, iTickSize) {
17327         this.yTicks = [];
17328         this.yTickSize = iTickSize;
17329
17330         var tickMap = {};
17331
17332         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17333             if (!tickMap[i]) {
17334                 this.yTicks[this.yTicks.length] = i;
17335                 tickMap[i] = true;
17336             }
17337         }
17338
17339         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17340             if (!tickMap[i]) {
17341                 this.yTicks[this.yTicks.length] = i;
17342                 tickMap[i] = true;
17343             }
17344         }
17345
17346         this.yTicks.sort(this.DDM.numericSort) ;
17347     },
17348
17349     /**
17350      * By default, the element can be dragged any place on the screen.  Use
17351      * this method to limit the horizontal travel of the element.  Pass in
17352      * 0,0 for the parameters if you want to lock the drag to the y axis.
17353      * @method setXConstraint
17354      * @param {int} iLeft the number of pixels the element can move to the left
17355      * @param {int} iRight the number of pixels the element can move to the
17356      * right
17357      * @param {int} iTickSize optional parameter for specifying that the
17358      * element
17359      * should move iTickSize pixels at a time.
17360      */
17361     setXConstraint: function(iLeft, iRight, iTickSize) {
17362         this.leftConstraint = iLeft;
17363         this.rightConstraint = iRight;
17364
17365         this.minX = this.initPageX - iLeft;
17366         this.maxX = this.initPageX + iRight;
17367         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17368
17369         this.constrainX = true;
17370     },
17371
17372     /**
17373      * Clears any constraints applied to this instance.  Also clears ticks
17374      * since they can't exist independent of a constraint at this time.
17375      * @method clearConstraints
17376      */
17377     clearConstraints: function() {
17378         this.constrainX = false;
17379         this.constrainY = false;
17380         this.clearTicks();
17381     },
17382
17383     /**
17384      * Clears any tick interval defined for this instance
17385      * @method clearTicks
17386      */
17387     clearTicks: function() {
17388         this.xTicks = null;
17389         this.yTicks = null;
17390         this.xTickSize = 0;
17391         this.yTickSize = 0;
17392     },
17393
17394     /**
17395      * By default, the element can be dragged any place on the screen.  Set
17396      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17397      * parameters if you want to lock the drag to the x axis.
17398      * @method setYConstraint
17399      * @param {int} iUp the number of pixels the element can move up
17400      * @param {int} iDown the number of pixels the element can move down
17401      * @param {int} iTickSize optional parameter for specifying that the
17402      * element should move iTickSize pixels at a time.
17403      */
17404     setYConstraint: function(iUp, iDown, iTickSize) {
17405         this.topConstraint = iUp;
17406         this.bottomConstraint = iDown;
17407
17408         this.minY = this.initPageY - iUp;
17409         this.maxY = this.initPageY + iDown;
17410         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17411
17412         this.constrainY = true;
17413
17414     },
17415
17416     /**
17417      * resetConstraints must be called if you manually reposition a dd element.
17418      * @method resetConstraints
17419      * @param {boolean} maintainOffset
17420      */
17421     resetConstraints: function() {
17422
17423
17424         // Maintain offsets if necessary
17425         if (this.initPageX || this.initPageX === 0) {
17426             // figure out how much this thing has moved
17427             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17428             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17429
17430             this.setInitPosition(dx, dy);
17431
17432         // This is the first time we have detected the element's position
17433         } else {
17434             this.setInitPosition();
17435         }
17436
17437         if (this.constrainX) {
17438             this.setXConstraint( this.leftConstraint,
17439                                  this.rightConstraint,
17440                                  this.xTickSize        );
17441         }
17442
17443         if (this.constrainY) {
17444             this.setYConstraint( this.topConstraint,
17445                                  this.bottomConstraint,
17446                                  this.yTickSize         );
17447         }
17448     },
17449
17450     /**
17451      * Normally the drag element is moved pixel by pixel, but we can specify
17452      * that it move a number of pixels at a time.  This method resolves the
17453      * location when we have it set up like this.
17454      * @method getTick
17455      * @param {int} val where we want to place the object
17456      * @param {int[]} tickArray sorted array of valid points
17457      * @return {int} the closest tick
17458      * @private
17459      */
17460     getTick: function(val, tickArray) {
17461
17462         if (!tickArray) {
17463             // If tick interval is not defined, it is effectively 1 pixel,
17464             // so we return the value passed to us.
17465             return val;
17466         } else if (tickArray[0] >= val) {
17467             // The value is lower than the first tick, so we return the first
17468             // tick.
17469             return tickArray[0];
17470         } else {
17471             for (var i=0, len=tickArray.length; i<len; ++i) {
17472                 var next = i + 1;
17473                 if (tickArray[next] && tickArray[next] >= val) {
17474                     var diff1 = val - tickArray[i];
17475                     var diff2 = tickArray[next] - val;
17476                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17477                 }
17478             }
17479
17480             // The value is larger than the last tick, so we return the last
17481             // tick.
17482             return tickArray[tickArray.length - 1];
17483         }
17484     },
17485
17486     /**
17487      * toString method
17488      * @method toString
17489      * @return {string} string representation of the dd obj
17490      */
17491     toString: function() {
17492         return ("DragDrop " + this.id);
17493     }
17494
17495 });
17496
17497 })();
17498 /*
17499  * Based on:
17500  * Ext JS Library 1.1.1
17501  * Copyright(c) 2006-2007, Ext JS, LLC.
17502  *
17503  * Originally Released Under LGPL - original licence link has changed is not relivant.
17504  *
17505  * Fork - LGPL
17506  * <script type="text/javascript">
17507  */
17508
17509
17510 /**
17511  * The drag and drop utility provides a framework for building drag and drop
17512  * applications.  In addition to enabling drag and drop for specific elements,
17513  * the drag and drop elements are tracked by the manager class, and the
17514  * interactions between the various elements are tracked during the drag and
17515  * the implementing code is notified about these important moments.
17516  */
17517
17518 // Only load the library once.  Rewriting the manager class would orphan
17519 // existing drag and drop instances.
17520 if (!Roo.dd.DragDropMgr) {
17521
17522 /**
17523  * @class Roo.dd.DragDropMgr
17524  * DragDropMgr is a singleton that tracks the element interaction for
17525  * all DragDrop items in the window.  Generally, you will not call
17526  * this class directly, but it does have helper methods that could
17527  * be useful in your DragDrop implementations.
17528  * @singleton
17529  */
17530 Roo.dd.DragDropMgr = function() {
17531
17532     var Event = Roo.EventManager;
17533
17534     return {
17535
17536         /**
17537          * Two dimensional Array of registered DragDrop objects.  The first
17538          * dimension is the DragDrop item group, the second the DragDrop
17539          * object.
17540          * @property ids
17541          * @type {string: string}
17542          * @private
17543          * @static
17544          */
17545         ids: {},
17546
17547         /**
17548          * Array of element ids defined as drag handles.  Used to determine
17549          * if the element that generated the mousedown event is actually the
17550          * handle and not the html element itself.
17551          * @property handleIds
17552          * @type {string: string}
17553          * @private
17554          * @static
17555          */
17556         handleIds: {},
17557
17558         /**
17559          * the DragDrop object that is currently being dragged
17560          * @property dragCurrent
17561          * @type DragDrop
17562          * @private
17563          * @static
17564          **/
17565         dragCurrent: null,
17566
17567         /**
17568          * the DragDrop object(s) that are being hovered over
17569          * @property dragOvers
17570          * @type Array
17571          * @private
17572          * @static
17573          */
17574         dragOvers: {},
17575
17576         /**
17577          * the X distance between the cursor and the object being dragged
17578          * @property deltaX
17579          * @type int
17580          * @private
17581          * @static
17582          */
17583         deltaX: 0,
17584
17585         /**
17586          * the Y distance between the cursor and the object being dragged
17587          * @property deltaY
17588          * @type int
17589          * @private
17590          * @static
17591          */
17592         deltaY: 0,
17593
17594         /**
17595          * Flag to determine if we should prevent the default behavior of the
17596          * events we define. By default this is true, but this can be set to
17597          * false if you need the default behavior (not recommended)
17598          * @property preventDefault
17599          * @type boolean
17600          * @static
17601          */
17602         preventDefault: true,
17603
17604         /**
17605          * Flag to determine if we should stop the propagation of the events
17606          * we generate. This is true by default but you may want to set it to
17607          * false if the html element contains other features that require the
17608          * mouse click.
17609          * @property stopPropagation
17610          * @type boolean
17611          * @static
17612          */
17613         stopPropagation: true,
17614
17615         /**
17616          * Internal flag that is set to true when drag and drop has been
17617          * intialized
17618          * @property initialized
17619          * @private
17620          * @static
17621          */
17622         initalized: false,
17623
17624         /**
17625          * All drag and drop can be disabled.
17626          * @property locked
17627          * @private
17628          * @static
17629          */
17630         locked: false,
17631
17632         /**
17633          * Called the first time an element is registered.
17634          * @method init
17635          * @private
17636          * @static
17637          */
17638         init: function() {
17639             this.initialized = true;
17640         },
17641
17642         /**
17643          * In point mode, drag and drop interaction is defined by the
17644          * location of the cursor during the drag/drop
17645          * @property POINT
17646          * @type int
17647          * @static
17648          */
17649         POINT: 0,
17650
17651         /**
17652          * In intersect mode, drag and drop interactio nis defined by the
17653          * overlap of two or more drag and drop objects.
17654          * @property INTERSECT
17655          * @type int
17656          * @static
17657          */
17658         INTERSECT: 1,
17659
17660         /**
17661          * The current drag and drop mode.  Default: POINT
17662          * @property mode
17663          * @type int
17664          * @static
17665          */
17666         mode: 0,
17667
17668         /**
17669          * Runs method on all drag and drop objects
17670          * @method _execOnAll
17671          * @private
17672          * @static
17673          */
17674         _execOnAll: function(sMethod, args) {
17675             for (var i in this.ids) {
17676                 for (var j in this.ids[i]) {
17677                     var oDD = this.ids[i][j];
17678                     if (! this.isTypeOfDD(oDD)) {
17679                         continue;
17680                     }
17681                     oDD[sMethod].apply(oDD, args);
17682                 }
17683             }
17684         },
17685
17686         /**
17687          * Drag and drop initialization.  Sets up the global event handlers
17688          * @method _onLoad
17689          * @private
17690          * @static
17691          */
17692         _onLoad: function() {
17693
17694             this.init();
17695
17696             if (!Roo.isTouch) {
17697                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17698                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17699             }
17700             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17701             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17702             
17703             Event.on(window,   "unload",    this._onUnload, this, true);
17704             Event.on(window,   "resize",    this._onResize, this, true);
17705             // Event.on(window,   "mouseout",    this._test);
17706
17707         },
17708
17709         /**
17710          * Reset constraints on all drag and drop objs
17711          * @method _onResize
17712          * @private
17713          * @static
17714          */
17715         _onResize: function(e) {
17716             this._execOnAll("resetConstraints", []);
17717         },
17718
17719         /**
17720          * Lock all drag and drop functionality
17721          * @method lock
17722          * @static
17723          */
17724         lock: function() { this.locked = true; },
17725
17726         /**
17727          * Unlock all drag and drop functionality
17728          * @method unlock
17729          * @static
17730          */
17731         unlock: function() { this.locked = false; },
17732
17733         /**
17734          * Is drag and drop locked?
17735          * @method isLocked
17736          * @return {boolean} True if drag and drop is locked, false otherwise.
17737          * @static
17738          */
17739         isLocked: function() { return this.locked; },
17740
17741         /**
17742          * Location cache that is set for all drag drop objects when a drag is
17743          * initiated, cleared when the drag is finished.
17744          * @property locationCache
17745          * @private
17746          * @static
17747          */
17748         locationCache: {},
17749
17750         /**
17751          * Set useCache to false if you want to force object the lookup of each
17752          * drag and drop linked element constantly during a drag.
17753          * @property useCache
17754          * @type boolean
17755          * @static
17756          */
17757         useCache: true,
17758
17759         /**
17760          * The number of pixels that the mouse needs to move after the
17761          * mousedown before the drag is initiated.  Default=3;
17762          * @property clickPixelThresh
17763          * @type int
17764          * @static
17765          */
17766         clickPixelThresh: 3,
17767
17768         /**
17769          * The number of milliseconds after the mousedown event to initiate the
17770          * drag if we don't get a mouseup event. Default=1000
17771          * @property clickTimeThresh
17772          * @type int
17773          * @static
17774          */
17775         clickTimeThresh: 350,
17776
17777         /**
17778          * Flag that indicates that either the drag pixel threshold or the
17779          * mousdown time threshold has been met
17780          * @property dragThreshMet
17781          * @type boolean
17782          * @private
17783          * @static
17784          */
17785         dragThreshMet: false,
17786
17787         /**
17788          * Timeout used for the click time threshold
17789          * @property clickTimeout
17790          * @type Object
17791          * @private
17792          * @static
17793          */
17794         clickTimeout: null,
17795
17796         /**
17797          * The X position of the mousedown event stored for later use when a
17798          * drag threshold is met.
17799          * @property startX
17800          * @type int
17801          * @private
17802          * @static
17803          */
17804         startX: 0,
17805
17806         /**
17807          * The Y position of the mousedown event stored for later use when a
17808          * drag threshold is met.
17809          * @property startY
17810          * @type int
17811          * @private
17812          * @static
17813          */
17814         startY: 0,
17815
17816         /**
17817          * Each DragDrop instance must be registered with the DragDropMgr.
17818          * This is executed in DragDrop.init()
17819          * @method regDragDrop
17820          * @param {DragDrop} oDD the DragDrop object to register
17821          * @param {String} sGroup the name of the group this element belongs to
17822          * @static
17823          */
17824         regDragDrop: function(oDD, sGroup) {
17825             if (!this.initialized) { this.init(); }
17826
17827             if (!this.ids[sGroup]) {
17828                 this.ids[sGroup] = {};
17829             }
17830             this.ids[sGroup][oDD.id] = oDD;
17831         },
17832
17833         /**
17834          * Removes the supplied dd instance from the supplied group. Executed
17835          * by DragDrop.removeFromGroup, so don't call this function directly.
17836          * @method removeDDFromGroup
17837          * @private
17838          * @static
17839          */
17840         removeDDFromGroup: function(oDD, sGroup) {
17841             if (!this.ids[sGroup]) {
17842                 this.ids[sGroup] = {};
17843             }
17844
17845             var obj = this.ids[sGroup];
17846             if (obj && obj[oDD.id]) {
17847                 delete obj[oDD.id];
17848             }
17849         },
17850
17851         /**
17852          * Unregisters a drag and drop item.  This is executed in
17853          * DragDrop.unreg, use that method instead of calling this directly.
17854          * @method _remove
17855          * @private
17856          * @static
17857          */
17858         _remove: function(oDD) {
17859             for (var g in oDD.groups) {
17860                 if (g && this.ids[g][oDD.id]) {
17861                     delete this.ids[g][oDD.id];
17862                 }
17863             }
17864             delete this.handleIds[oDD.id];
17865         },
17866
17867         /**
17868          * Each DragDrop handle element must be registered.  This is done
17869          * automatically when executing DragDrop.setHandleElId()
17870          * @method regHandle
17871          * @param {String} sDDId the DragDrop id this element is a handle for
17872          * @param {String} sHandleId the id of the element that is the drag
17873          * handle
17874          * @static
17875          */
17876         regHandle: function(sDDId, sHandleId) {
17877             if (!this.handleIds[sDDId]) {
17878                 this.handleIds[sDDId] = {};
17879             }
17880             this.handleIds[sDDId][sHandleId] = sHandleId;
17881         },
17882
17883         /**
17884          * Utility function to determine if a given element has been
17885          * registered as a drag drop item.
17886          * @method isDragDrop
17887          * @param {String} id the element id to check
17888          * @return {boolean} true if this element is a DragDrop item,
17889          * false otherwise
17890          * @static
17891          */
17892         isDragDrop: function(id) {
17893             return ( this.getDDById(id) ) ? true : false;
17894         },
17895
17896         /**
17897          * Returns the drag and drop instances that are in all groups the
17898          * passed in instance belongs to.
17899          * @method getRelated
17900          * @param {DragDrop} p_oDD the obj to get related data for
17901          * @param {boolean} bTargetsOnly if true, only return targetable objs
17902          * @return {DragDrop[]} the related instances
17903          * @static
17904          */
17905         getRelated: function(p_oDD, bTargetsOnly) {
17906             var oDDs = [];
17907             for (var i in p_oDD.groups) {
17908                 for (j in this.ids[i]) {
17909                     var dd = this.ids[i][j];
17910                     if (! this.isTypeOfDD(dd)) {
17911                         continue;
17912                     }
17913                     if (!bTargetsOnly || dd.isTarget) {
17914                         oDDs[oDDs.length] = dd;
17915                     }
17916                 }
17917             }
17918
17919             return oDDs;
17920         },
17921
17922         /**
17923          * Returns true if the specified dd target is a legal target for
17924          * the specifice drag obj
17925          * @method isLegalTarget
17926          * @param {DragDrop} the drag obj
17927          * @param {DragDrop} the target
17928          * @return {boolean} true if the target is a legal target for the
17929          * dd obj
17930          * @static
17931          */
17932         isLegalTarget: function (oDD, oTargetDD) {
17933             var targets = this.getRelated(oDD, true);
17934             for (var i=0, len=targets.length;i<len;++i) {
17935                 if (targets[i].id == oTargetDD.id) {
17936                     return true;
17937                 }
17938             }
17939
17940             return false;
17941         },
17942
17943         /**
17944          * My goal is to be able to transparently determine if an object is
17945          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17946          * returns "object", oDD.constructor.toString() always returns
17947          * "DragDrop" and not the name of the subclass.  So for now it just
17948          * evaluates a well-known variable in DragDrop.
17949          * @method isTypeOfDD
17950          * @param {Object} the object to evaluate
17951          * @return {boolean} true if typeof oDD = DragDrop
17952          * @static
17953          */
17954         isTypeOfDD: function (oDD) {
17955             return (oDD && oDD.__ygDragDrop);
17956         },
17957
17958         /**
17959          * Utility function to determine if a given element has been
17960          * registered as a drag drop handle for the given Drag Drop object.
17961          * @method isHandle
17962          * @param {String} id the element id to check
17963          * @return {boolean} true if this element is a DragDrop handle, false
17964          * otherwise
17965          * @static
17966          */
17967         isHandle: function(sDDId, sHandleId) {
17968             return ( this.handleIds[sDDId] &&
17969                             this.handleIds[sDDId][sHandleId] );
17970         },
17971
17972         /**
17973          * Returns the DragDrop instance for a given id
17974          * @method getDDById
17975          * @param {String} id the id of the DragDrop object
17976          * @return {DragDrop} the drag drop object, null if it is not found
17977          * @static
17978          */
17979         getDDById: function(id) {
17980             for (var i in this.ids) {
17981                 if (this.ids[i][id]) {
17982                     return this.ids[i][id];
17983                 }
17984             }
17985             return null;
17986         },
17987
17988         /**
17989          * Fired after a registered DragDrop object gets the mousedown event.
17990          * Sets up the events required to track the object being dragged
17991          * @method handleMouseDown
17992          * @param {Event} e the event
17993          * @param oDD the DragDrop object being dragged
17994          * @private
17995          * @static
17996          */
17997         handleMouseDown: function(e, oDD) {
17998             if(Roo.QuickTips){
17999                 Roo.QuickTips.disable();
18000             }
18001             this.currentTarget = e.getTarget();
18002
18003             this.dragCurrent = oDD;
18004
18005             var el = oDD.getEl();
18006
18007             // track start position
18008             this.startX = e.getPageX();
18009             this.startY = e.getPageY();
18010
18011             this.deltaX = this.startX - el.offsetLeft;
18012             this.deltaY = this.startY - el.offsetTop;
18013
18014             this.dragThreshMet = false;
18015
18016             this.clickTimeout = setTimeout(
18017                     function() {
18018                         var DDM = Roo.dd.DDM;
18019                         DDM.startDrag(DDM.startX, DDM.startY);
18020                     },
18021                     this.clickTimeThresh );
18022         },
18023
18024         /**
18025          * Fired when either the drag pixel threshol or the mousedown hold
18026          * time threshold has been met.
18027          * @method startDrag
18028          * @param x {int} the X position of the original mousedown
18029          * @param y {int} the Y position of the original mousedown
18030          * @static
18031          */
18032         startDrag: function(x, y) {
18033             clearTimeout(this.clickTimeout);
18034             if (this.dragCurrent) {
18035                 this.dragCurrent.b4StartDrag(x, y);
18036                 this.dragCurrent.startDrag(x, y);
18037             }
18038             this.dragThreshMet = true;
18039         },
18040
18041         /**
18042          * Internal function to handle the mouseup event.  Will be invoked
18043          * from the context of the document.
18044          * @method handleMouseUp
18045          * @param {Event} e the event
18046          * @private
18047          * @static
18048          */
18049         handleMouseUp: function(e) {
18050
18051             if(Roo.QuickTips){
18052                 Roo.QuickTips.enable();
18053             }
18054             if (! this.dragCurrent) {
18055                 return;
18056             }
18057
18058             clearTimeout(this.clickTimeout);
18059
18060             if (this.dragThreshMet) {
18061                 this.fireEvents(e, true);
18062             } else {
18063             }
18064
18065             this.stopDrag(e);
18066
18067             this.stopEvent(e);
18068         },
18069
18070         /**
18071          * Utility to stop event propagation and event default, if these
18072          * features are turned on.
18073          * @method stopEvent
18074          * @param {Event} e the event as returned by this.getEvent()
18075          * @static
18076          */
18077         stopEvent: function(e){
18078             if(this.stopPropagation) {
18079                 e.stopPropagation();
18080             }
18081
18082             if (this.preventDefault) {
18083                 e.preventDefault();
18084             }
18085         },
18086
18087         /**
18088          * Internal function to clean up event handlers after the drag
18089          * operation is complete
18090          * @method stopDrag
18091          * @param {Event} e the event
18092          * @private
18093          * @static
18094          */
18095         stopDrag: function(e) {
18096             // Fire the drag end event for the item that was dragged
18097             if (this.dragCurrent) {
18098                 if (this.dragThreshMet) {
18099                     this.dragCurrent.b4EndDrag(e);
18100                     this.dragCurrent.endDrag(e);
18101                 }
18102
18103                 this.dragCurrent.onMouseUp(e);
18104             }
18105
18106             this.dragCurrent = null;
18107             this.dragOvers = {};
18108         },
18109
18110         /**
18111          * Internal function to handle the mousemove event.  Will be invoked
18112          * from the context of the html element.
18113          *
18114          * @TODO figure out what we can do about mouse events lost when the
18115          * user drags objects beyond the window boundary.  Currently we can
18116          * detect this in internet explorer by verifying that the mouse is
18117          * down during the mousemove event.  Firefox doesn't give us the
18118          * button state on the mousemove event.
18119          * @method handleMouseMove
18120          * @param {Event} e the event
18121          * @private
18122          * @static
18123          */
18124         handleMouseMove: function(e) {
18125             if (! this.dragCurrent) {
18126                 return true;
18127             }
18128
18129             // var button = e.which || e.button;
18130
18131             // check for IE mouseup outside of page boundary
18132             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18133                 this.stopEvent(e);
18134                 return this.handleMouseUp(e);
18135             }
18136
18137             if (!this.dragThreshMet) {
18138                 var diffX = Math.abs(this.startX - e.getPageX());
18139                 var diffY = Math.abs(this.startY - e.getPageY());
18140                 if (diffX > this.clickPixelThresh ||
18141                             diffY > this.clickPixelThresh) {
18142                     this.startDrag(this.startX, this.startY);
18143                 }
18144             }
18145
18146             if (this.dragThreshMet) {
18147                 this.dragCurrent.b4Drag(e);
18148                 this.dragCurrent.onDrag(e);
18149                 if(!this.dragCurrent.moveOnly){
18150                     this.fireEvents(e, false);
18151                 }
18152             }
18153
18154             this.stopEvent(e);
18155
18156             return true;
18157         },
18158
18159         /**
18160          * Iterates over all of the DragDrop elements to find ones we are
18161          * hovering over or dropping on
18162          * @method fireEvents
18163          * @param {Event} e the event
18164          * @param {boolean} isDrop is this a drop op or a mouseover op?
18165          * @private
18166          * @static
18167          */
18168         fireEvents: function(e, isDrop) {
18169             var dc = this.dragCurrent;
18170
18171             // If the user did the mouse up outside of the window, we could
18172             // get here even though we have ended the drag.
18173             if (!dc || dc.isLocked()) {
18174                 return;
18175             }
18176
18177             var pt = e.getPoint();
18178
18179             // cache the previous dragOver array
18180             var oldOvers = [];
18181
18182             var outEvts   = [];
18183             var overEvts  = [];
18184             var dropEvts  = [];
18185             var enterEvts = [];
18186
18187             // Check to see if the object(s) we were hovering over is no longer
18188             // being hovered over so we can fire the onDragOut event
18189             for (var i in this.dragOvers) {
18190
18191                 var ddo = this.dragOvers[i];
18192
18193                 if (! this.isTypeOfDD(ddo)) {
18194                     continue;
18195                 }
18196
18197                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18198                     outEvts.push( ddo );
18199                 }
18200
18201                 oldOvers[i] = true;
18202                 delete this.dragOvers[i];
18203             }
18204
18205             for (var sGroup in dc.groups) {
18206
18207                 if ("string" != typeof sGroup) {
18208                     continue;
18209                 }
18210
18211                 for (i in this.ids[sGroup]) {
18212                     var oDD = this.ids[sGroup][i];
18213                     if (! this.isTypeOfDD(oDD)) {
18214                         continue;
18215                     }
18216
18217                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18218                         if (this.isOverTarget(pt, oDD, this.mode)) {
18219                             // look for drop interactions
18220                             if (isDrop) {
18221                                 dropEvts.push( oDD );
18222                             // look for drag enter and drag over interactions
18223                             } else {
18224
18225                                 // initial drag over: dragEnter fires
18226                                 if (!oldOvers[oDD.id]) {
18227                                     enterEvts.push( oDD );
18228                                 // subsequent drag overs: dragOver fires
18229                                 } else {
18230                                     overEvts.push( oDD );
18231                                 }
18232
18233                                 this.dragOvers[oDD.id] = oDD;
18234                             }
18235                         }
18236                     }
18237                 }
18238             }
18239
18240             if (this.mode) {
18241                 if (outEvts.length) {
18242                     dc.b4DragOut(e, outEvts);
18243                     dc.onDragOut(e, outEvts);
18244                 }
18245
18246                 if (enterEvts.length) {
18247                     dc.onDragEnter(e, enterEvts);
18248                 }
18249
18250                 if (overEvts.length) {
18251                     dc.b4DragOver(e, overEvts);
18252                     dc.onDragOver(e, overEvts);
18253                 }
18254
18255                 if (dropEvts.length) {
18256                     dc.b4DragDrop(e, dropEvts);
18257                     dc.onDragDrop(e, dropEvts);
18258                 }
18259
18260             } else {
18261                 // fire dragout events
18262                 var len = 0;
18263                 for (i=0, len=outEvts.length; i<len; ++i) {
18264                     dc.b4DragOut(e, outEvts[i].id);
18265                     dc.onDragOut(e, outEvts[i].id);
18266                 }
18267
18268                 // fire enter events
18269                 for (i=0,len=enterEvts.length; i<len; ++i) {
18270                     // dc.b4DragEnter(e, oDD.id);
18271                     dc.onDragEnter(e, enterEvts[i].id);
18272                 }
18273
18274                 // fire over events
18275                 for (i=0,len=overEvts.length; i<len; ++i) {
18276                     dc.b4DragOver(e, overEvts[i].id);
18277                     dc.onDragOver(e, overEvts[i].id);
18278                 }
18279
18280                 // fire drop events
18281                 for (i=0, len=dropEvts.length; i<len; ++i) {
18282                     dc.b4DragDrop(e, dropEvts[i].id);
18283                     dc.onDragDrop(e, dropEvts[i].id);
18284                 }
18285
18286             }
18287
18288             // notify about a drop that did not find a target
18289             if (isDrop && !dropEvts.length) {
18290                 dc.onInvalidDrop(e);
18291             }
18292
18293         },
18294
18295         /**
18296          * Helper function for getting the best match from the list of drag
18297          * and drop objects returned by the drag and drop events when we are
18298          * in INTERSECT mode.  It returns either the first object that the
18299          * cursor is over, or the object that has the greatest overlap with
18300          * the dragged element.
18301          * @method getBestMatch
18302          * @param  {DragDrop[]} dds The array of drag and drop objects
18303          * targeted
18304          * @return {DragDrop}       The best single match
18305          * @static
18306          */
18307         getBestMatch: function(dds) {
18308             var winner = null;
18309             // Return null if the input is not what we expect
18310             //if (!dds || !dds.length || dds.length == 0) {
18311                // winner = null;
18312             // If there is only one item, it wins
18313             //} else if (dds.length == 1) {
18314
18315             var len = dds.length;
18316
18317             if (len == 1) {
18318                 winner = dds[0];
18319             } else {
18320                 // Loop through the targeted items
18321                 for (var i=0; i<len; ++i) {
18322                     var dd = dds[i];
18323                     // If the cursor is over the object, it wins.  If the
18324                     // cursor is over multiple matches, the first one we come
18325                     // to wins.
18326                     if (dd.cursorIsOver) {
18327                         winner = dd;
18328                         break;
18329                     // Otherwise the object with the most overlap wins
18330                     } else {
18331                         if (!winner ||
18332                             winner.overlap.getArea() < dd.overlap.getArea()) {
18333                             winner = dd;
18334                         }
18335                     }
18336                 }
18337             }
18338
18339             return winner;
18340         },
18341
18342         /**
18343          * Refreshes the cache of the top-left and bottom-right points of the
18344          * drag and drop objects in the specified group(s).  This is in the
18345          * format that is stored in the drag and drop instance, so typical
18346          * usage is:
18347          * <code>
18348          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18349          * </code>
18350          * Alternatively:
18351          * <code>
18352          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18353          * </code>
18354          * @TODO this really should be an indexed array.  Alternatively this
18355          * method could accept both.
18356          * @method refreshCache
18357          * @param {Object} groups an associative array of groups to refresh
18358          * @static
18359          */
18360         refreshCache: function(groups) {
18361             for (var sGroup in groups) {
18362                 if ("string" != typeof sGroup) {
18363                     continue;
18364                 }
18365                 for (var i in this.ids[sGroup]) {
18366                     var oDD = this.ids[sGroup][i];
18367
18368                     if (this.isTypeOfDD(oDD)) {
18369                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18370                         var loc = this.getLocation(oDD);
18371                         if (loc) {
18372                             this.locationCache[oDD.id] = loc;
18373                         } else {
18374                             delete this.locationCache[oDD.id];
18375                             // this will unregister the drag and drop object if
18376                             // the element is not in a usable state
18377                             // oDD.unreg();
18378                         }
18379                     }
18380                 }
18381             }
18382         },
18383
18384         /**
18385          * This checks to make sure an element exists and is in the DOM.  The
18386          * main purpose is to handle cases where innerHTML is used to remove
18387          * drag and drop objects from the DOM.  IE provides an 'unspecified
18388          * error' when trying to access the offsetParent of such an element
18389          * @method verifyEl
18390          * @param {HTMLElement} el the element to check
18391          * @return {boolean} true if the element looks usable
18392          * @static
18393          */
18394         verifyEl: function(el) {
18395             if (el) {
18396                 var parent;
18397                 if(Roo.isIE){
18398                     try{
18399                         parent = el.offsetParent;
18400                     }catch(e){}
18401                 }else{
18402                     parent = el.offsetParent;
18403                 }
18404                 if (parent) {
18405                     return true;
18406                 }
18407             }
18408
18409             return false;
18410         },
18411
18412         /**
18413          * Returns a Region object containing the drag and drop element's position
18414          * and size, including the padding configured for it
18415          * @method getLocation
18416          * @param {DragDrop} oDD the drag and drop object to get the
18417          *                       location for
18418          * @return {Roo.lib.Region} a Region object representing the total area
18419          *                             the element occupies, including any padding
18420          *                             the instance is configured for.
18421          * @static
18422          */
18423         getLocation: function(oDD) {
18424             if (! this.isTypeOfDD(oDD)) {
18425                 return null;
18426             }
18427
18428             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18429
18430             try {
18431                 pos= Roo.lib.Dom.getXY(el);
18432             } catch (e) { }
18433
18434             if (!pos) {
18435                 return null;
18436             }
18437
18438             x1 = pos[0];
18439             x2 = x1 + el.offsetWidth;
18440             y1 = pos[1];
18441             y2 = y1 + el.offsetHeight;
18442
18443             t = y1 - oDD.padding[0];
18444             r = x2 + oDD.padding[1];
18445             b = y2 + oDD.padding[2];
18446             l = x1 - oDD.padding[3];
18447
18448             return new Roo.lib.Region( t, r, b, l );
18449         },
18450
18451         /**
18452          * Checks the cursor location to see if it over the target
18453          * @method isOverTarget
18454          * @param {Roo.lib.Point} pt The point to evaluate
18455          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18456          * @return {boolean} true if the mouse is over the target
18457          * @private
18458          * @static
18459          */
18460         isOverTarget: function(pt, oTarget, intersect) {
18461             // use cache if available
18462             var loc = this.locationCache[oTarget.id];
18463             if (!loc || !this.useCache) {
18464                 loc = this.getLocation(oTarget);
18465                 this.locationCache[oTarget.id] = loc;
18466
18467             }
18468
18469             if (!loc) {
18470                 return false;
18471             }
18472
18473             oTarget.cursorIsOver = loc.contains( pt );
18474
18475             // DragDrop is using this as a sanity check for the initial mousedown
18476             // in this case we are done.  In POINT mode, if the drag obj has no
18477             // contraints, we are also done. Otherwise we need to evaluate the
18478             // location of the target as related to the actual location of the
18479             // dragged element.
18480             var dc = this.dragCurrent;
18481             if (!dc || !dc.getTargetCoord ||
18482                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18483                 return oTarget.cursorIsOver;
18484             }
18485
18486             oTarget.overlap = null;
18487
18488             // Get the current location of the drag element, this is the
18489             // location of the mouse event less the delta that represents
18490             // where the original mousedown happened on the element.  We
18491             // need to consider constraints and ticks as well.
18492             var pos = dc.getTargetCoord(pt.x, pt.y);
18493
18494             var el = dc.getDragEl();
18495             var curRegion = new Roo.lib.Region( pos.y,
18496                                                    pos.x + el.offsetWidth,
18497                                                    pos.y + el.offsetHeight,
18498                                                    pos.x );
18499
18500             var overlap = curRegion.intersect(loc);
18501
18502             if (overlap) {
18503                 oTarget.overlap = overlap;
18504                 return (intersect) ? true : oTarget.cursorIsOver;
18505             } else {
18506                 return false;
18507             }
18508         },
18509
18510         /**
18511          * unload event handler
18512          * @method _onUnload
18513          * @private
18514          * @static
18515          */
18516         _onUnload: function(e, me) {
18517             Roo.dd.DragDropMgr.unregAll();
18518         },
18519
18520         /**
18521          * Cleans up the drag and drop events and objects.
18522          * @method unregAll
18523          * @private
18524          * @static
18525          */
18526         unregAll: function() {
18527
18528             if (this.dragCurrent) {
18529                 this.stopDrag();
18530                 this.dragCurrent = null;
18531             }
18532
18533             this._execOnAll("unreg", []);
18534
18535             for (i in this.elementCache) {
18536                 delete this.elementCache[i];
18537             }
18538
18539             this.elementCache = {};
18540             this.ids = {};
18541         },
18542
18543         /**
18544          * A cache of DOM elements
18545          * @property elementCache
18546          * @private
18547          * @static
18548          */
18549         elementCache: {},
18550
18551         /**
18552          * Get the wrapper for the DOM element specified
18553          * @method getElWrapper
18554          * @param {String} id the id of the element to get
18555          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18556          * @private
18557          * @deprecated This wrapper isn't that useful
18558          * @static
18559          */
18560         getElWrapper: function(id) {
18561             var oWrapper = this.elementCache[id];
18562             if (!oWrapper || !oWrapper.el) {
18563                 oWrapper = this.elementCache[id] =
18564                     new this.ElementWrapper(Roo.getDom(id));
18565             }
18566             return oWrapper;
18567         },
18568
18569         /**
18570          * Returns the actual DOM element
18571          * @method getElement
18572          * @param {String} id the id of the elment to get
18573          * @return {Object} The element
18574          * @deprecated use Roo.getDom instead
18575          * @static
18576          */
18577         getElement: function(id) {
18578             return Roo.getDom(id);
18579         },
18580
18581         /**
18582          * Returns the style property for the DOM element (i.e.,
18583          * document.getElById(id).style)
18584          * @method getCss
18585          * @param {String} id the id of the elment to get
18586          * @return {Object} The style property of the element
18587          * @deprecated use Roo.getDom instead
18588          * @static
18589          */
18590         getCss: function(id) {
18591             var el = Roo.getDom(id);
18592             return (el) ? el.style : null;
18593         },
18594
18595         /**
18596          * Inner class for cached elements
18597          * @class DragDropMgr.ElementWrapper
18598          * @for DragDropMgr
18599          * @private
18600          * @deprecated
18601          */
18602         ElementWrapper: function(el) {
18603                 /**
18604                  * The element
18605                  * @property el
18606                  */
18607                 this.el = el || null;
18608                 /**
18609                  * The element id
18610                  * @property id
18611                  */
18612                 this.id = this.el && el.id;
18613                 /**
18614                  * A reference to the style property
18615                  * @property css
18616                  */
18617                 this.css = this.el && el.style;
18618             },
18619
18620         /**
18621          * Returns the X position of an html element
18622          * @method getPosX
18623          * @param el the element for which to get the position
18624          * @return {int} the X coordinate
18625          * @for DragDropMgr
18626          * @deprecated use Roo.lib.Dom.getX instead
18627          * @static
18628          */
18629         getPosX: function(el) {
18630             return Roo.lib.Dom.getX(el);
18631         },
18632
18633         /**
18634          * Returns the Y position of an html element
18635          * @method getPosY
18636          * @param el the element for which to get the position
18637          * @return {int} the Y coordinate
18638          * @deprecated use Roo.lib.Dom.getY instead
18639          * @static
18640          */
18641         getPosY: function(el) {
18642             return Roo.lib.Dom.getY(el);
18643         },
18644
18645         /**
18646          * Swap two nodes.  In IE, we use the native method, for others we
18647          * emulate the IE behavior
18648          * @method swapNode
18649          * @param n1 the first node to swap
18650          * @param n2 the other node to swap
18651          * @static
18652          */
18653         swapNode: function(n1, n2) {
18654             if (n1.swapNode) {
18655                 n1.swapNode(n2);
18656             } else {
18657                 var p = n2.parentNode;
18658                 var s = n2.nextSibling;
18659
18660                 if (s == n1) {
18661                     p.insertBefore(n1, n2);
18662                 } else if (n2 == n1.nextSibling) {
18663                     p.insertBefore(n2, n1);
18664                 } else {
18665                     n1.parentNode.replaceChild(n2, n1);
18666                     p.insertBefore(n1, s);
18667                 }
18668             }
18669         },
18670
18671         /**
18672          * Returns the current scroll position
18673          * @method getScroll
18674          * @private
18675          * @static
18676          */
18677         getScroll: function () {
18678             var t, l, dde=document.documentElement, db=document.body;
18679             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18680                 t = dde.scrollTop;
18681                 l = dde.scrollLeft;
18682             } else if (db) {
18683                 t = db.scrollTop;
18684                 l = db.scrollLeft;
18685             } else {
18686
18687             }
18688             return { top: t, left: l };
18689         },
18690
18691         /**
18692          * Returns the specified element style property
18693          * @method getStyle
18694          * @param {HTMLElement} el          the element
18695          * @param {string}      styleProp   the style property
18696          * @return {string} The value of the style property
18697          * @deprecated use Roo.lib.Dom.getStyle
18698          * @static
18699          */
18700         getStyle: function(el, styleProp) {
18701             return Roo.fly(el).getStyle(styleProp);
18702         },
18703
18704         /**
18705          * Gets the scrollTop
18706          * @method getScrollTop
18707          * @return {int} the document's scrollTop
18708          * @static
18709          */
18710         getScrollTop: function () { return this.getScroll().top; },
18711
18712         /**
18713          * Gets the scrollLeft
18714          * @method getScrollLeft
18715          * @return {int} the document's scrollTop
18716          * @static
18717          */
18718         getScrollLeft: function () { return this.getScroll().left; },
18719
18720         /**
18721          * Sets the x/y position of an element to the location of the
18722          * target element.
18723          * @method moveToEl
18724          * @param {HTMLElement} moveEl      The element to move
18725          * @param {HTMLElement} targetEl    The position reference element
18726          * @static
18727          */
18728         moveToEl: function (moveEl, targetEl) {
18729             var aCoord = Roo.lib.Dom.getXY(targetEl);
18730             Roo.lib.Dom.setXY(moveEl, aCoord);
18731         },
18732
18733         /**
18734          * Numeric array sort function
18735          * @method numericSort
18736          * @static
18737          */
18738         numericSort: function(a, b) { return (a - b); },
18739
18740         /**
18741          * Internal counter
18742          * @property _timeoutCount
18743          * @private
18744          * @static
18745          */
18746         _timeoutCount: 0,
18747
18748         /**
18749          * Trying to make the load order less important.  Without this we get
18750          * an error if this file is loaded before the Event Utility.
18751          * @method _addListeners
18752          * @private
18753          * @static
18754          */
18755         _addListeners: function() {
18756             var DDM = Roo.dd.DDM;
18757             if ( Roo.lib.Event && document ) {
18758                 DDM._onLoad();
18759             } else {
18760                 if (DDM._timeoutCount > 2000) {
18761                 } else {
18762                     setTimeout(DDM._addListeners, 10);
18763                     if (document && document.body) {
18764                         DDM._timeoutCount += 1;
18765                     }
18766                 }
18767             }
18768         },
18769
18770         /**
18771          * Recursively searches the immediate parent and all child nodes for
18772          * the handle element in order to determine wheter or not it was
18773          * clicked.
18774          * @method handleWasClicked
18775          * @param node the html element to inspect
18776          * @static
18777          */
18778         handleWasClicked: function(node, id) {
18779             if (this.isHandle(id, node.id)) {
18780                 return true;
18781             } else {
18782                 // check to see if this is a text node child of the one we want
18783                 var p = node.parentNode;
18784
18785                 while (p) {
18786                     if (this.isHandle(id, p.id)) {
18787                         return true;
18788                     } else {
18789                         p = p.parentNode;
18790                     }
18791                 }
18792             }
18793
18794             return false;
18795         }
18796
18797     };
18798
18799 }();
18800
18801 // shorter alias, save a few bytes
18802 Roo.dd.DDM = Roo.dd.DragDropMgr;
18803 Roo.dd.DDM._addListeners();
18804
18805 }/*
18806  * Based on:
18807  * Ext JS Library 1.1.1
18808  * Copyright(c) 2006-2007, Ext JS, LLC.
18809  *
18810  * Originally Released Under LGPL - original licence link has changed is not relivant.
18811  *
18812  * Fork - LGPL
18813  * <script type="text/javascript">
18814  */
18815
18816 /**
18817  * @class Roo.dd.DD
18818  * A DragDrop implementation where the linked element follows the
18819  * mouse cursor during a drag.
18820  * @extends Roo.dd.DragDrop
18821  * @constructor
18822  * @param {String} id the id of the linked element
18823  * @param {String} sGroup the group of related DragDrop items
18824  * @param {object} config an object containing configurable attributes
18825  *                Valid properties for DD:
18826  *                    scroll
18827  */
18828 Roo.dd.DD = function(id, sGroup, config) {
18829     if (id) {
18830         this.init(id, sGroup, config);
18831     }
18832 };
18833
18834 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18835
18836     /**
18837      * When set to true, the utility automatically tries to scroll the browser
18838      * window wehn a drag and drop element is dragged near the viewport boundary.
18839      * Defaults to true.
18840      * @property scroll
18841      * @type boolean
18842      */
18843     scroll: true,
18844
18845     /**
18846      * Sets the pointer offset to the distance between the linked element's top
18847      * left corner and the location the element was clicked
18848      * @method autoOffset
18849      * @param {int} iPageX the X coordinate of the click
18850      * @param {int} iPageY the Y coordinate of the click
18851      */
18852     autoOffset: function(iPageX, iPageY) {
18853         var x = iPageX - this.startPageX;
18854         var y = iPageY - this.startPageY;
18855         this.setDelta(x, y);
18856     },
18857
18858     /**
18859      * Sets the pointer offset.  You can call this directly to force the
18860      * offset to be in a particular location (e.g., pass in 0,0 to set it
18861      * to the center of the object)
18862      * @method setDelta
18863      * @param {int} iDeltaX the distance from the left
18864      * @param {int} iDeltaY the distance from the top
18865      */
18866     setDelta: function(iDeltaX, iDeltaY) {
18867         this.deltaX = iDeltaX;
18868         this.deltaY = iDeltaY;
18869     },
18870
18871     /**
18872      * Sets the drag element to the location of the mousedown or click event,
18873      * maintaining the cursor location relative to the location on the element
18874      * that was clicked.  Override this if you want to place the element in a
18875      * location other than where the cursor is.
18876      * @method setDragElPos
18877      * @param {int} iPageX the X coordinate of the mousedown or drag event
18878      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18879      */
18880     setDragElPos: function(iPageX, iPageY) {
18881         // the first time we do this, we are going to check to make sure
18882         // the element has css positioning
18883
18884         var el = this.getDragEl();
18885         this.alignElWithMouse(el, iPageX, iPageY);
18886     },
18887
18888     /**
18889      * Sets the element to the location of the mousedown or click event,
18890      * maintaining the cursor location relative to the location on the element
18891      * that was clicked.  Override this if you want to place the element in a
18892      * location other than where the cursor is.
18893      * @method alignElWithMouse
18894      * @param {HTMLElement} el the element to move
18895      * @param {int} iPageX the X coordinate of the mousedown or drag event
18896      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18897      */
18898     alignElWithMouse: function(el, iPageX, iPageY) {
18899         var oCoord = this.getTargetCoord(iPageX, iPageY);
18900         var fly = el.dom ? el : Roo.fly(el);
18901         if (!this.deltaSetXY) {
18902             var aCoord = [oCoord.x, oCoord.y];
18903             fly.setXY(aCoord);
18904             var newLeft = fly.getLeft(true);
18905             var newTop  = fly.getTop(true);
18906             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18907         } else {
18908             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18909         }
18910
18911         this.cachePosition(oCoord.x, oCoord.y);
18912         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18913         return oCoord;
18914     },
18915
18916     /**
18917      * Saves the most recent position so that we can reset the constraints and
18918      * tick marks on-demand.  We need to know this so that we can calculate the
18919      * number of pixels the element is offset from its original position.
18920      * @method cachePosition
18921      * @param iPageX the current x position (optional, this just makes it so we
18922      * don't have to look it up again)
18923      * @param iPageY the current y position (optional, this just makes it so we
18924      * don't have to look it up again)
18925      */
18926     cachePosition: function(iPageX, iPageY) {
18927         if (iPageX) {
18928             this.lastPageX = iPageX;
18929             this.lastPageY = iPageY;
18930         } else {
18931             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18932             this.lastPageX = aCoord[0];
18933             this.lastPageY = aCoord[1];
18934         }
18935     },
18936
18937     /**
18938      * Auto-scroll the window if the dragged object has been moved beyond the
18939      * visible window boundary.
18940      * @method autoScroll
18941      * @param {int} x the drag element's x position
18942      * @param {int} y the drag element's y position
18943      * @param {int} h the height of the drag element
18944      * @param {int} w the width of the drag element
18945      * @private
18946      */
18947     autoScroll: function(x, y, h, w) {
18948
18949         if (this.scroll) {
18950             // The client height
18951             var clientH = Roo.lib.Dom.getViewWidth();
18952
18953             // The client width
18954             var clientW = Roo.lib.Dom.getViewHeight();
18955
18956             // The amt scrolled down
18957             var st = this.DDM.getScrollTop();
18958
18959             // The amt scrolled right
18960             var sl = this.DDM.getScrollLeft();
18961
18962             // Location of the bottom of the element
18963             var bot = h + y;
18964
18965             // Location of the right of the element
18966             var right = w + x;
18967
18968             // The distance from the cursor to the bottom of the visible area,
18969             // adjusted so that we don't scroll if the cursor is beyond the
18970             // element drag constraints
18971             var toBot = (clientH + st - y - this.deltaY);
18972
18973             // The distance from the cursor to the right of the visible area
18974             var toRight = (clientW + sl - x - this.deltaX);
18975
18976
18977             // How close to the edge the cursor must be before we scroll
18978             // var thresh = (document.all) ? 100 : 40;
18979             var thresh = 40;
18980
18981             // How many pixels to scroll per autoscroll op.  This helps to reduce
18982             // clunky scrolling. IE is more sensitive about this ... it needs this
18983             // value to be higher.
18984             var scrAmt = (document.all) ? 80 : 30;
18985
18986             // Scroll down if we are near the bottom of the visible page and the
18987             // obj extends below the crease
18988             if ( bot > clientH && toBot < thresh ) {
18989                 window.scrollTo(sl, st + scrAmt);
18990             }
18991
18992             // Scroll up if the window is scrolled down and the top of the object
18993             // goes above the top border
18994             if ( y < st && st > 0 && y - st < thresh ) {
18995                 window.scrollTo(sl, st - scrAmt);
18996             }
18997
18998             // Scroll right if the obj is beyond the right border and the cursor is
18999             // near the border.
19000             if ( right > clientW && toRight < thresh ) {
19001                 window.scrollTo(sl + scrAmt, st);
19002             }
19003
19004             // Scroll left if the window has been scrolled to the right and the obj
19005             // extends past the left border
19006             if ( x < sl && sl > 0 && x - sl < thresh ) {
19007                 window.scrollTo(sl - scrAmt, st);
19008             }
19009         }
19010     },
19011
19012     /**
19013      * Finds the location the element should be placed if we want to move
19014      * it to where the mouse location less the click offset would place us.
19015      * @method getTargetCoord
19016      * @param {int} iPageX the X coordinate of the click
19017      * @param {int} iPageY the Y coordinate of the click
19018      * @return an object that contains the coordinates (Object.x and Object.y)
19019      * @private
19020      */
19021     getTargetCoord: function(iPageX, iPageY) {
19022
19023
19024         var x = iPageX - this.deltaX;
19025         var y = iPageY - this.deltaY;
19026
19027         if (this.constrainX) {
19028             if (x < this.minX) { x = this.minX; }
19029             if (x > this.maxX) { x = this.maxX; }
19030         }
19031
19032         if (this.constrainY) {
19033             if (y < this.minY) { y = this.minY; }
19034             if (y > this.maxY) { y = this.maxY; }
19035         }
19036
19037         x = this.getTick(x, this.xTicks);
19038         y = this.getTick(y, this.yTicks);
19039
19040
19041         return {x:x, y:y};
19042     },
19043
19044     /*
19045      * Sets up config options specific to this class. Overrides
19046      * Roo.dd.DragDrop, but all versions of this method through the
19047      * inheritance chain are called
19048      */
19049     applyConfig: function() {
19050         Roo.dd.DD.superclass.applyConfig.call(this);
19051         this.scroll = (this.config.scroll !== false);
19052     },
19053
19054     /*
19055      * Event that fires prior to the onMouseDown event.  Overrides
19056      * Roo.dd.DragDrop.
19057      */
19058     b4MouseDown: function(e) {
19059         // this.resetConstraints();
19060         this.autoOffset(e.getPageX(),
19061                             e.getPageY());
19062     },
19063
19064     /*
19065      * Event that fires prior to the onDrag event.  Overrides
19066      * Roo.dd.DragDrop.
19067      */
19068     b4Drag: function(e) {
19069         this.setDragElPos(e.getPageX(),
19070                             e.getPageY());
19071     },
19072
19073     toString: function() {
19074         return ("DD " + this.id);
19075     }
19076
19077     //////////////////////////////////////////////////////////////////////////
19078     // Debugging ygDragDrop events that can be overridden
19079     //////////////////////////////////////////////////////////////////////////
19080     /*
19081     startDrag: function(x, y) {
19082     },
19083
19084     onDrag: function(e) {
19085     },
19086
19087     onDragEnter: function(e, id) {
19088     },
19089
19090     onDragOver: function(e, id) {
19091     },
19092
19093     onDragOut: function(e, id) {
19094     },
19095
19096     onDragDrop: function(e, id) {
19097     },
19098
19099     endDrag: function(e) {
19100     }
19101
19102     */
19103
19104 });/*
19105  * Based on:
19106  * Ext JS Library 1.1.1
19107  * Copyright(c) 2006-2007, Ext JS, LLC.
19108  *
19109  * Originally Released Under LGPL - original licence link has changed is not relivant.
19110  *
19111  * Fork - LGPL
19112  * <script type="text/javascript">
19113  */
19114
19115 /**
19116  * @class Roo.dd.DDProxy
19117  * A DragDrop implementation that inserts an empty, bordered div into
19118  * the document that follows the cursor during drag operations.  At the time of
19119  * the click, the frame div is resized to the dimensions of the linked html
19120  * element, and moved to the exact location of the linked element.
19121  *
19122  * References to the "frame" element refer to the single proxy element that
19123  * was created to be dragged in place of all DDProxy elements on the
19124  * page.
19125  *
19126  * @extends Roo.dd.DD
19127  * @constructor
19128  * @param {String} id the id of the linked html element
19129  * @param {String} sGroup the group of related DragDrop objects
19130  * @param {object} config an object containing configurable attributes
19131  *                Valid properties for DDProxy in addition to those in DragDrop:
19132  *                   resizeFrame, centerFrame, dragElId
19133  */
19134 Roo.dd.DDProxy = function(id, sGroup, config) {
19135     if (id) {
19136         this.init(id, sGroup, config);
19137         this.initFrame();
19138     }
19139 };
19140
19141 /**
19142  * The default drag frame div id
19143  * @property Roo.dd.DDProxy.dragElId
19144  * @type String
19145  * @static
19146  */
19147 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19148
19149 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19150
19151     /**
19152      * By default we resize the drag frame to be the same size as the element
19153      * we want to drag (this is to get the frame effect).  We can turn it off
19154      * if we want a different behavior.
19155      * @property resizeFrame
19156      * @type boolean
19157      */
19158     resizeFrame: true,
19159
19160     /**
19161      * By default the frame is positioned exactly where the drag element is, so
19162      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19163      * you do not have constraints on the obj is to have the drag frame centered
19164      * around the cursor.  Set centerFrame to true for this effect.
19165      * @property centerFrame
19166      * @type boolean
19167      */
19168     centerFrame: false,
19169
19170     /**
19171      * Creates the proxy element if it does not yet exist
19172      * @method createFrame
19173      */
19174     createFrame: function() {
19175         var self = this;
19176         var body = document.body;
19177
19178         if (!body || !body.firstChild) {
19179             setTimeout( function() { self.createFrame(); }, 50 );
19180             return;
19181         }
19182
19183         var div = this.getDragEl();
19184
19185         if (!div) {
19186             div    = document.createElement("div");
19187             div.id = this.dragElId;
19188             var s  = div.style;
19189
19190             s.position   = "absolute";
19191             s.visibility = "hidden";
19192             s.cursor     = "move";
19193             s.border     = "2px solid #aaa";
19194             s.zIndex     = 999;
19195
19196             // appendChild can blow up IE if invoked prior to the window load event
19197             // while rendering a table.  It is possible there are other scenarios
19198             // that would cause this to happen as well.
19199             body.insertBefore(div, body.firstChild);
19200         }
19201     },
19202
19203     /**
19204      * Initialization for the drag frame element.  Must be called in the
19205      * constructor of all subclasses
19206      * @method initFrame
19207      */
19208     initFrame: function() {
19209         this.createFrame();
19210     },
19211
19212     applyConfig: function() {
19213         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19214
19215         this.resizeFrame = (this.config.resizeFrame !== false);
19216         this.centerFrame = (this.config.centerFrame);
19217         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19218     },
19219
19220     /**
19221      * Resizes the drag frame to the dimensions of the clicked object, positions
19222      * it over the object, and finally displays it
19223      * @method showFrame
19224      * @param {int} iPageX X click position
19225      * @param {int} iPageY Y click position
19226      * @private
19227      */
19228     showFrame: function(iPageX, iPageY) {
19229         var el = this.getEl();
19230         var dragEl = this.getDragEl();
19231         var s = dragEl.style;
19232
19233         this._resizeProxy();
19234
19235         if (this.centerFrame) {
19236             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19237                            Math.round(parseInt(s.height, 10)/2) );
19238         }
19239
19240         this.setDragElPos(iPageX, iPageY);
19241
19242         Roo.fly(dragEl).show();
19243     },
19244
19245     /**
19246      * The proxy is automatically resized to the dimensions of the linked
19247      * element when a drag is initiated, unless resizeFrame is set to false
19248      * @method _resizeProxy
19249      * @private
19250      */
19251     _resizeProxy: function() {
19252         if (this.resizeFrame) {
19253             var el = this.getEl();
19254             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19255         }
19256     },
19257
19258     // overrides Roo.dd.DragDrop
19259     b4MouseDown: function(e) {
19260         var x = e.getPageX();
19261         var y = e.getPageY();
19262         this.autoOffset(x, y);
19263         this.setDragElPos(x, y);
19264     },
19265
19266     // overrides Roo.dd.DragDrop
19267     b4StartDrag: function(x, y) {
19268         // show the drag frame
19269         this.showFrame(x, y);
19270     },
19271
19272     // overrides Roo.dd.DragDrop
19273     b4EndDrag: function(e) {
19274         Roo.fly(this.getDragEl()).hide();
19275     },
19276
19277     // overrides Roo.dd.DragDrop
19278     // By default we try to move the element to the last location of the frame.
19279     // This is so that the default behavior mirrors that of Roo.dd.DD.
19280     endDrag: function(e) {
19281
19282         var lel = this.getEl();
19283         var del = this.getDragEl();
19284
19285         // Show the drag frame briefly so we can get its position
19286         del.style.visibility = "";
19287
19288         this.beforeMove();
19289         // Hide the linked element before the move to get around a Safari
19290         // rendering bug.
19291         lel.style.visibility = "hidden";
19292         Roo.dd.DDM.moveToEl(lel, del);
19293         del.style.visibility = "hidden";
19294         lel.style.visibility = "";
19295
19296         this.afterDrag();
19297     },
19298
19299     beforeMove : function(){
19300
19301     },
19302
19303     afterDrag : function(){
19304
19305     },
19306
19307     toString: function() {
19308         return ("DDProxy " + this.id);
19309     }
19310
19311 });
19312 /*
19313  * Based on:
19314  * Ext JS Library 1.1.1
19315  * Copyright(c) 2006-2007, Ext JS, LLC.
19316  *
19317  * Originally Released Under LGPL - original licence link has changed is not relivant.
19318  *
19319  * Fork - LGPL
19320  * <script type="text/javascript">
19321  */
19322
19323  /**
19324  * @class Roo.dd.DDTarget
19325  * A DragDrop implementation that does not move, but can be a drop
19326  * target.  You would get the same result by simply omitting implementation
19327  * for the event callbacks, but this way we reduce the processing cost of the
19328  * event listener and the callbacks.
19329  * @extends Roo.dd.DragDrop
19330  * @constructor
19331  * @param {String} id the id of the element that is a drop target
19332  * @param {String} sGroup the group of related DragDrop objects
19333  * @param {object} config an object containing configurable attributes
19334  *                 Valid properties for DDTarget in addition to those in
19335  *                 DragDrop:
19336  *                    none
19337  */
19338 Roo.dd.DDTarget = function(id, sGroup, config) {
19339     if (id) {
19340         this.initTarget(id, sGroup, config);
19341     }
19342     if (config.listeners || config.events) { 
19343        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19344             listeners : config.listeners || {}, 
19345             events : config.events || {} 
19346         });    
19347     }
19348 };
19349
19350 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19351 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19352     toString: function() {
19353         return ("DDTarget " + this.id);
19354     }
19355 });
19356 /*
19357  * Based on:
19358  * Ext JS Library 1.1.1
19359  * Copyright(c) 2006-2007, Ext JS, LLC.
19360  *
19361  * Originally Released Under LGPL - original licence link has changed is not relivant.
19362  *
19363  * Fork - LGPL
19364  * <script type="text/javascript">
19365  */
19366  
19367
19368 /**
19369  * @class Roo.dd.ScrollManager
19370  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19371  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19372  * @singleton
19373  */
19374 Roo.dd.ScrollManager = function(){
19375     var ddm = Roo.dd.DragDropMgr;
19376     var els = {};
19377     var dragEl = null;
19378     var proc = {};
19379     
19380     
19381     
19382     var onStop = function(e){
19383         dragEl = null;
19384         clearProc();
19385     };
19386     
19387     var triggerRefresh = function(){
19388         if(ddm.dragCurrent){
19389              ddm.refreshCache(ddm.dragCurrent.groups);
19390         }
19391     };
19392     
19393     var doScroll = function(){
19394         if(ddm.dragCurrent){
19395             var dds = Roo.dd.ScrollManager;
19396             if(!dds.animate){
19397                 if(proc.el.scroll(proc.dir, dds.increment)){
19398                     triggerRefresh();
19399                 }
19400             }else{
19401                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19402             }
19403         }
19404     };
19405     
19406     var clearProc = function(){
19407         if(proc.id){
19408             clearInterval(proc.id);
19409         }
19410         proc.id = 0;
19411         proc.el = null;
19412         proc.dir = "";
19413     };
19414     
19415     var startProc = function(el, dir){
19416          Roo.log('scroll startproc');
19417         clearProc();
19418         proc.el = el;
19419         proc.dir = dir;
19420         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19421     };
19422     
19423     var onFire = function(e, isDrop){
19424        
19425         if(isDrop || !ddm.dragCurrent){ return; }
19426         var dds = Roo.dd.ScrollManager;
19427         if(!dragEl || dragEl != ddm.dragCurrent){
19428             dragEl = ddm.dragCurrent;
19429             // refresh regions on drag start
19430             dds.refreshCache();
19431         }
19432         
19433         var xy = Roo.lib.Event.getXY(e);
19434         var pt = new Roo.lib.Point(xy[0], xy[1]);
19435         for(var id in els){
19436             var el = els[id], r = el._region;
19437             if(r && r.contains(pt) && el.isScrollable()){
19438                 if(r.bottom - pt.y <= dds.thresh){
19439                     if(proc.el != el){
19440                         startProc(el, "down");
19441                     }
19442                     return;
19443                 }else if(r.right - pt.x <= dds.thresh){
19444                     if(proc.el != el){
19445                         startProc(el, "left");
19446                     }
19447                     return;
19448                 }else if(pt.y - r.top <= dds.thresh){
19449                     if(proc.el != el){
19450                         startProc(el, "up");
19451                     }
19452                     return;
19453                 }else if(pt.x - r.left <= dds.thresh){
19454                     if(proc.el != el){
19455                         startProc(el, "right");
19456                     }
19457                     return;
19458                 }
19459             }
19460         }
19461         clearProc();
19462     };
19463     
19464     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19465     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19466     
19467     return {
19468         /**
19469          * Registers new overflow element(s) to auto scroll
19470          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19471          */
19472         register : function(el){
19473             if(el instanceof Array){
19474                 for(var i = 0, len = el.length; i < len; i++) {
19475                         this.register(el[i]);
19476                 }
19477             }else{
19478                 el = Roo.get(el);
19479                 els[el.id] = el;
19480             }
19481             Roo.dd.ScrollManager.els = els;
19482         },
19483         
19484         /**
19485          * Unregisters overflow element(s) so they are no longer scrolled
19486          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19487          */
19488         unregister : function(el){
19489             if(el instanceof Array){
19490                 for(var i = 0, len = el.length; i < len; i++) {
19491                         this.unregister(el[i]);
19492                 }
19493             }else{
19494                 el = Roo.get(el);
19495                 delete els[el.id];
19496             }
19497         },
19498         
19499         /**
19500          * The number of pixels from the edge of a container the pointer needs to be to 
19501          * trigger scrolling (defaults to 25)
19502          * @type Number
19503          */
19504         thresh : 25,
19505         
19506         /**
19507          * The number of pixels to scroll in each scroll increment (defaults to 50)
19508          * @type Number
19509          */
19510         increment : 100,
19511         
19512         /**
19513          * The frequency of scrolls in milliseconds (defaults to 500)
19514          * @type Number
19515          */
19516         frequency : 500,
19517         
19518         /**
19519          * True to animate the scroll (defaults to true)
19520          * @type Boolean
19521          */
19522         animate: true,
19523         
19524         /**
19525          * The animation duration in seconds - 
19526          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19527          * @type Number
19528          */
19529         animDuration: .4,
19530         
19531         /**
19532          * Manually trigger a cache refresh.
19533          */
19534         refreshCache : function(){
19535             for(var id in els){
19536                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19537                     els[id]._region = els[id].getRegion();
19538                 }
19539             }
19540         }
19541     };
19542 }();/*
19543  * Based on:
19544  * Ext JS Library 1.1.1
19545  * Copyright(c) 2006-2007, Ext JS, LLC.
19546  *
19547  * Originally Released Under LGPL - original licence link has changed is not relivant.
19548  *
19549  * Fork - LGPL
19550  * <script type="text/javascript">
19551  */
19552  
19553
19554 /**
19555  * @class Roo.dd.Registry
19556  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19557  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19558  * @singleton
19559  */
19560 Roo.dd.Registry = function(){
19561     var elements = {}; 
19562     var handles = {}; 
19563     var autoIdSeed = 0;
19564
19565     var getId = function(el, autogen){
19566         if(typeof el == "string"){
19567             return el;
19568         }
19569         var id = el.id;
19570         if(!id && autogen !== false){
19571             id = "roodd-" + (++autoIdSeed);
19572             el.id = id;
19573         }
19574         return id;
19575     };
19576     
19577     return {
19578     /**
19579      * Register a drag drop element
19580      * @param {String|HTMLElement} element The id or DOM node to register
19581      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19582      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19583      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19584      * populated in the data object (if applicable):
19585      * <pre>
19586 Value      Description<br />
19587 ---------  ------------------------------------------<br />
19588 handles    Array of DOM nodes that trigger dragging<br />
19589            for the element being registered<br />
19590 isHandle   True if the element passed in triggers<br />
19591            dragging itself, else false
19592 </pre>
19593      */
19594         register : function(el, data){
19595             data = data || {};
19596             if(typeof el == "string"){
19597                 el = document.getElementById(el);
19598             }
19599             data.ddel = el;
19600             elements[getId(el)] = data;
19601             if(data.isHandle !== false){
19602                 handles[data.ddel.id] = data;
19603             }
19604             if(data.handles){
19605                 var hs = data.handles;
19606                 for(var i = 0, len = hs.length; i < len; i++){
19607                         handles[getId(hs[i])] = data;
19608                 }
19609             }
19610         },
19611
19612     /**
19613      * Unregister a drag drop element
19614      * @param {String|HTMLElement}  element The id or DOM node to unregister
19615      */
19616         unregister : function(el){
19617             var id = getId(el, false);
19618             var data = elements[id];
19619             if(data){
19620                 delete elements[id];
19621                 if(data.handles){
19622                     var hs = data.handles;
19623                     for(var i = 0, len = hs.length; i < len; i++){
19624                         delete handles[getId(hs[i], false)];
19625                     }
19626                 }
19627             }
19628         },
19629
19630     /**
19631      * Returns the handle registered for a DOM Node by id
19632      * @param {String|HTMLElement} id The DOM node or id to look up
19633      * @return {Object} handle The custom handle data
19634      */
19635         getHandle : function(id){
19636             if(typeof id != "string"){ // must be element?
19637                 id = id.id;
19638             }
19639             return handles[id];
19640         },
19641
19642     /**
19643      * Returns the handle that is registered for the DOM node that is the target of the event
19644      * @param {Event} e The event
19645      * @return {Object} handle The custom handle data
19646      */
19647         getHandleFromEvent : function(e){
19648             var t = Roo.lib.Event.getTarget(e);
19649             return t ? handles[t.id] : null;
19650         },
19651
19652     /**
19653      * Returns a custom data object that is registered for a DOM node by id
19654      * @param {String|HTMLElement} id The DOM node or id to look up
19655      * @return {Object} data The custom data
19656      */
19657         getTarget : function(id){
19658             if(typeof id != "string"){ // must be element?
19659                 id = id.id;
19660             }
19661             return elements[id];
19662         },
19663
19664     /**
19665      * Returns a custom data object that is registered for the DOM node that is the target of the event
19666      * @param {Event} e The event
19667      * @return {Object} data The custom data
19668      */
19669         getTargetFromEvent : function(e){
19670             var t = Roo.lib.Event.getTarget(e);
19671             return t ? elements[t.id] || handles[t.id] : null;
19672         }
19673     };
19674 }();/*
19675  * Based on:
19676  * Ext JS Library 1.1.1
19677  * Copyright(c) 2006-2007, Ext JS, LLC.
19678  *
19679  * Originally Released Under LGPL - original licence link has changed is not relivant.
19680  *
19681  * Fork - LGPL
19682  * <script type="text/javascript">
19683  */
19684  
19685
19686 /**
19687  * @class Roo.dd.StatusProxy
19688  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19689  * default drag proxy used by all Roo.dd components.
19690  * @constructor
19691  * @param {Object} config
19692  */
19693 Roo.dd.StatusProxy = function(config){
19694     Roo.apply(this, config);
19695     this.id = this.id || Roo.id();
19696     this.el = new Roo.Layer({
19697         dh: {
19698             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19699                 {tag: "div", cls: "x-dd-drop-icon"},
19700                 {tag: "div", cls: "x-dd-drag-ghost"}
19701             ]
19702         }, 
19703         shadow: !config || config.shadow !== false
19704     });
19705     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19706     this.dropStatus = this.dropNotAllowed;
19707 };
19708
19709 Roo.dd.StatusProxy.prototype = {
19710     /**
19711      * @cfg {String} dropAllowed
19712      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19713      */
19714     dropAllowed : "x-dd-drop-ok",
19715     /**
19716      * @cfg {String} dropNotAllowed
19717      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19718      */
19719     dropNotAllowed : "x-dd-drop-nodrop",
19720
19721     /**
19722      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19723      * over the current target element.
19724      * @param {String} cssClass The css class for the new drop status indicator image
19725      */
19726     setStatus : function(cssClass){
19727         cssClass = cssClass || this.dropNotAllowed;
19728         if(this.dropStatus != cssClass){
19729             this.el.replaceClass(this.dropStatus, cssClass);
19730             this.dropStatus = cssClass;
19731         }
19732     },
19733
19734     /**
19735      * Resets the status indicator to the default dropNotAllowed value
19736      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19737      */
19738     reset : function(clearGhost){
19739         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19740         this.dropStatus = this.dropNotAllowed;
19741         if(clearGhost){
19742             this.ghost.update("");
19743         }
19744     },
19745
19746     /**
19747      * Updates the contents of the ghost element
19748      * @param {String} html The html that will replace the current innerHTML of the ghost element
19749      */
19750     update : function(html){
19751         if(typeof html == "string"){
19752             this.ghost.update(html);
19753         }else{
19754             this.ghost.update("");
19755             html.style.margin = "0";
19756             this.ghost.dom.appendChild(html);
19757         }
19758         // ensure float = none set?? cant remember why though.
19759         var el = this.ghost.dom.firstChild;
19760                 if(el){
19761                         Roo.fly(el).setStyle('float', 'none');
19762                 }
19763     },
19764     
19765     /**
19766      * Returns the underlying proxy {@link Roo.Layer}
19767      * @return {Roo.Layer} el
19768     */
19769     getEl : function(){
19770         return this.el;
19771     },
19772
19773     /**
19774      * Returns the ghost element
19775      * @return {Roo.Element} el
19776      */
19777     getGhost : function(){
19778         return this.ghost;
19779     },
19780
19781     /**
19782      * Hides the proxy
19783      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19784      */
19785     hide : function(clear){
19786         this.el.hide();
19787         if(clear){
19788             this.reset(true);
19789         }
19790     },
19791
19792     /**
19793      * Stops the repair animation if it's currently running
19794      */
19795     stop : function(){
19796         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19797             this.anim.stop();
19798         }
19799     },
19800
19801     /**
19802      * Displays this proxy
19803      */
19804     show : function(){
19805         this.el.show();
19806     },
19807
19808     /**
19809      * Force the Layer to sync its shadow and shim positions to the element
19810      */
19811     sync : function(){
19812         this.el.sync();
19813     },
19814
19815     /**
19816      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19817      * invalid drop operation by the item being dragged.
19818      * @param {Array} xy The XY position of the element ([x, y])
19819      * @param {Function} callback The function to call after the repair is complete
19820      * @param {Object} scope The scope in which to execute the callback
19821      */
19822     repair : function(xy, callback, scope){
19823         this.callback = callback;
19824         this.scope = scope;
19825         if(xy && this.animRepair !== false){
19826             this.el.addClass("x-dd-drag-repair");
19827             this.el.hideUnders(true);
19828             this.anim = this.el.shift({
19829                 duration: this.repairDuration || .5,
19830                 easing: 'easeOut',
19831                 xy: xy,
19832                 stopFx: true,
19833                 callback: this.afterRepair,
19834                 scope: this
19835             });
19836         }else{
19837             this.afterRepair();
19838         }
19839     },
19840
19841     // private
19842     afterRepair : function(){
19843         this.hide(true);
19844         if(typeof this.callback == "function"){
19845             this.callback.call(this.scope || this);
19846         }
19847         this.callback = null;
19848         this.scope = null;
19849     }
19850 };/*
19851  * Based on:
19852  * Ext JS Library 1.1.1
19853  * Copyright(c) 2006-2007, Ext JS, LLC.
19854  *
19855  * Originally Released Under LGPL - original licence link has changed is not relivant.
19856  *
19857  * Fork - LGPL
19858  * <script type="text/javascript">
19859  */
19860
19861 /**
19862  * @class Roo.dd.DragSource
19863  * @extends Roo.dd.DDProxy
19864  * A simple class that provides the basic implementation needed to make any element draggable.
19865  * @constructor
19866  * @param {String/HTMLElement/Element} el The container element
19867  * @param {Object} config
19868  */
19869 Roo.dd.DragSource = function(el, config){
19870     this.el = Roo.get(el);
19871     this.dragData = {};
19872     
19873     Roo.apply(this, config);
19874     
19875     if(!this.proxy){
19876         this.proxy = new Roo.dd.StatusProxy();
19877     }
19878
19879     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19880           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19881     
19882     this.dragging = false;
19883 };
19884
19885 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19886     /**
19887      * @cfg {String} dropAllowed
19888      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19889      */
19890     dropAllowed : "x-dd-drop-ok",
19891     /**
19892      * @cfg {String} dropNotAllowed
19893      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19894      */
19895     dropNotAllowed : "x-dd-drop-nodrop",
19896
19897     /**
19898      * Returns the data object associated with this drag source
19899      * @return {Object} data An object containing arbitrary data
19900      */
19901     getDragData : function(e){
19902         return this.dragData;
19903     },
19904
19905     // private
19906     onDragEnter : function(e, id){
19907         var target = Roo.dd.DragDropMgr.getDDById(id);
19908         this.cachedTarget = target;
19909         if(this.beforeDragEnter(target, e, id) !== false){
19910             if(target.isNotifyTarget){
19911                 var status = target.notifyEnter(this, e, this.dragData);
19912                 this.proxy.setStatus(status);
19913             }else{
19914                 this.proxy.setStatus(this.dropAllowed);
19915             }
19916             
19917             if(this.afterDragEnter){
19918                 /**
19919                  * An empty function by default, but provided so that you can perform a custom action
19920                  * when the dragged item enters the drop target by providing an implementation.
19921                  * @param {Roo.dd.DragDrop} target The drop target
19922                  * @param {Event} e The event object
19923                  * @param {String} id The id of the dragged element
19924                  * @method afterDragEnter
19925                  */
19926                 this.afterDragEnter(target, e, id);
19927             }
19928         }
19929     },
19930
19931     /**
19932      * An empty function by default, but provided so that you can perform a custom action
19933      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19934      * @param {Roo.dd.DragDrop} target The drop target
19935      * @param {Event} e The event object
19936      * @param {String} id The id of the dragged element
19937      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19938      */
19939     beforeDragEnter : function(target, e, id){
19940         return true;
19941     },
19942
19943     // private
19944     alignElWithMouse: function() {
19945         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19946         this.proxy.sync();
19947     },
19948
19949     // private
19950     onDragOver : function(e, id){
19951         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19952         if(this.beforeDragOver(target, e, id) !== false){
19953             if(target.isNotifyTarget){
19954                 var status = target.notifyOver(this, e, this.dragData);
19955                 this.proxy.setStatus(status);
19956             }
19957
19958             if(this.afterDragOver){
19959                 /**
19960                  * An empty function by default, but provided so that you can perform a custom action
19961                  * while the dragged item is over the drop target by providing an implementation.
19962                  * @param {Roo.dd.DragDrop} target The drop target
19963                  * @param {Event} e The event object
19964                  * @param {String} id The id of the dragged element
19965                  * @method afterDragOver
19966                  */
19967                 this.afterDragOver(target, e, id);
19968             }
19969         }
19970     },
19971
19972     /**
19973      * An empty function by default, but provided so that you can perform a custom action
19974      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19975      * @param {Roo.dd.DragDrop} target The drop target
19976      * @param {Event} e The event object
19977      * @param {String} id The id of the dragged element
19978      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19979      */
19980     beforeDragOver : function(target, e, id){
19981         return true;
19982     },
19983
19984     // private
19985     onDragOut : function(e, id){
19986         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19987         if(this.beforeDragOut(target, e, id) !== false){
19988             if(target.isNotifyTarget){
19989                 target.notifyOut(this, e, this.dragData);
19990             }
19991             this.proxy.reset();
19992             if(this.afterDragOut){
19993                 /**
19994                  * An empty function by default, but provided so that you can perform a custom action
19995                  * after the dragged item is dragged out of the target without dropping.
19996                  * @param {Roo.dd.DragDrop} target The drop target
19997                  * @param {Event} e The event object
19998                  * @param {String} id The id of the dragged element
19999                  * @method afterDragOut
20000                  */
20001                 this.afterDragOut(target, e, id);
20002             }
20003         }
20004         this.cachedTarget = null;
20005     },
20006
20007     /**
20008      * An empty function by default, but provided so that you can perform a custom action before the dragged
20009      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20010      * @param {Roo.dd.DragDrop} target The drop target
20011      * @param {Event} e The event object
20012      * @param {String} id The id of the dragged element
20013      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20014      */
20015     beforeDragOut : function(target, e, id){
20016         return true;
20017     },
20018     
20019     // private
20020     onDragDrop : function(e, id){
20021         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20022         if(this.beforeDragDrop(target, e, id) !== false){
20023             if(target.isNotifyTarget){
20024                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20025                     this.onValidDrop(target, e, id);
20026                 }else{
20027                     this.onInvalidDrop(target, e, id);
20028                 }
20029             }else{
20030                 this.onValidDrop(target, e, id);
20031             }
20032             
20033             if(this.afterDragDrop){
20034                 /**
20035                  * An empty function by default, but provided so that you can perform a custom action
20036                  * after a valid drag drop has occurred by providing an implementation.
20037                  * @param {Roo.dd.DragDrop} target The drop target
20038                  * @param {Event} e The event object
20039                  * @param {String} id The id of the dropped element
20040                  * @method afterDragDrop
20041                  */
20042                 this.afterDragDrop(target, e, id);
20043             }
20044         }
20045         delete this.cachedTarget;
20046     },
20047
20048     /**
20049      * An empty function by default, but provided so that you can perform a custom action before the dragged
20050      * item is dropped onto the target and optionally cancel the onDragDrop.
20051      * @param {Roo.dd.DragDrop} target The drop target
20052      * @param {Event} e The event object
20053      * @param {String} id The id of the dragged element
20054      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20055      */
20056     beforeDragDrop : function(target, e, id){
20057         return true;
20058     },
20059
20060     // private
20061     onValidDrop : function(target, e, id){
20062         this.hideProxy();
20063         if(this.afterValidDrop){
20064             /**
20065              * An empty function by default, but provided so that you can perform a custom action
20066              * after a valid drop has occurred by providing an implementation.
20067              * @param {Object} target The target DD 
20068              * @param {Event} e The event object
20069              * @param {String} id The id of the dropped element
20070              * @method afterInvalidDrop
20071              */
20072             this.afterValidDrop(target, e, id);
20073         }
20074     },
20075
20076     // private
20077     getRepairXY : function(e, data){
20078         return this.el.getXY();  
20079     },
20080
20081     // private
20082     onInvalidDrop : function(target, e, id){
20083         this.beforeInvalidDrop(target, e, id);
20084         if(this.cachedTarget){
20085             if(this.cachedTarget.isNotifyTarget){
20086                 this.cachedTarget.notifyOut(this, e, this.dragData);
20087             }
20088             this.cacheTarget = null;
20089         }
20090         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20091
20092         if(this.afterInvalidDrop){
20093             /**
20094              * An empty function by default, but provided so that you can perform a custom action
20095              * after an invalid drop has occurred by providing an implementation.
20096              * @param {Event} e The event object
20097              * @param {String} id The id of the dropped element
20098              * @method afterInvalidDrop
20099              */
20100             this.afterInvalidDrop(e, id);
20101         }
20102     },
20103
20104     // private
20105     afterRepair : function(){
20106         if(Roo.enableFx){
20107             this.el.highlight(this.hlColor || "c3daf9");
20108         }
20109         this.dragging = false;
20110     },
20111
20112     /**
20113      * An empty function by default, but provided so that you can perform a custom action after an invalid
20114      * drop has occurred.
20115      * @param {Roo.dd.DragDrop} target The drop target
20116      * @param {Event} e The event object
20117      * @param {String} id The id of the dragged element
20118      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20119      */
20120     beforeInvalidDrop : function(target, e, id){
20121         return true;
20122     },
20123
20124     // private
20125     handleMouseDown : function(e){
20126         if(this.dragging) {
20127             return;
20128         }
20129         var data = this.getDragData(e);
20130         if(data && this.onBeforeDrag(data, e) !== false){
20131             this.dragData = data;
20132             this.proxy.stop();
20133             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20134         } 
20135     },
20136
20137     /**
20138      * An empty function by default, but provided so that you can perform a custom action before the initial
20139      * drag event begins and optionally cancel it.
20140      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20141      * @param {Event} e The event object
20142      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20143      */
20144     onBeforeDrag : function(data, e){
20145         return true;
20146     },
20147
20148     /**
20149      * An empty function by default, but provided so that you can perform a custom action once the initial
20150      * drag event has begun.  The drag cannot be canceled from this function.
20151      * @param {Number} x The x position of the click on the dragged object
20152      * @param {Number} y The y position of the click on the dragged object
20153      */
20154     onStartDrag : Roo.emptyFn,
20155
20156     // private - YUI override
20157     startDrag : function(x, y){
20158         this.proxy.reset();
20159         this.dragging = true;
20160         this.proxy.update("");
20161         this.onInitDrag(x, y);
20162         this.proxy.show();
20163     },
20164
20165     // private
20166     onInitDrag : function(x, y){
20167         var clone = this.el.dom.cloneNode(true);
20168         clone.id = Roo.id(); // prevent duplicate ids
20169         this.proxy.update(clone);
20170         this.onStartDrag(x, y);
20171         return true;
20172     },
20173
20174     /**
20175      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20176      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20177      */
20178     getProxy : function(){
20179         return this.proxy;  
20180     },
20181
20182     /**
20183      * Hides the drag source's {@link Roo.dd.StatusProxy}
20184      */
20185     hideProxy : function(){
20186         this.proxy.hide();  
20187         this.proxy.reset(true);
20188         this.dragging = false;
20189     },
20190
20191     // private
20192     triggerCacheRefresh : function(){
20193         Roo.dd.DDM.refreshCache(this.groups);
20194     },
20195
20196     // private - override to prevent hiding
20197     b4EndDrag: function(e) {
20198     },
20199
20200     // private - override to prevent moving
20201     endDrag : function(e){
20202         this.onEndDrag(this.dragData, e);
20203     },
20204
20205     // private
20206     onEndDrag : function(data, e){
20207     },
20208     
20209     // private - pin to cursor
20210     autoOffset : function(x, y) {
20211         this.setDelta(-12, -20);
20212     }    
20213 });/*
20214  * Based on:
20215  * Ext JS Library 1.1.1
20216  * Copyright(c) 2006-2007, Ext JS, LLC.
20217  *
20218  * Originally Released Under LGPL - original licence link has changed is not relivant.
20219  *
20220  * Fork - LGPL
20221  * <script type="text/javascript">
20222  */
20223
20224
20225 /**
20226  * @class Roo.dd.DropTarget
20227  * @extends Roo.dd.DDTarget
20228  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20229  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20230  * @constructor
20231  * @param {String/HTMLElement/Element} el The container element
20232  * @param {Object} config
20233  */
20234 Roo.dd.DropTarget = function(el, config){
20235     this.el = Roo.get(el);
20236     
20237     var listeners = false; ;
20238     if (config && config.listeners) {
20239         listeners= config.listeners;
20240         delete config.listeners;
20241     }
20242     Roo.apply(this, config);
20243     
20244     if(this.containerScroll){
20245         Roo.dd.ScrollManager.register(this.el);
20246     }
20247     this.addEvents( {
20248          /**
20249          * @scope Roo.dd.DropTarget
20250          */
20251          
20252          /**
20253          * @event enter
20254          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20255          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20256          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20257          * 
20258          * IMPORTANT : it should set this.overClass and this.dropAllowed
20259          * 
20260          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20261          * @param {Event} e The event
20262          * @param {Object} data An object containing arbitrary data supplied by the drag source
20263          */
20264         "enter" : true,
20265         
20266          /**
20267          * @event over
20268          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20269          * This method will be called on every mouse movement while the drag source is over the drop target.
20270          * This default implementation simply returns the dropAllowed config value.
20271          * 
20272          * IMPORTANT : it should set this.dropAllowed
20273          * 
20274          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20275          * @param {Event} e The event
20276          * @param {Object} data An object containing arbitrary data supplied by the drag source
20277          
20278          */
20279         "over" : true,
20280         /**
20281          * @event out
20282          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20283          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20284          * overClass (if any) from the drop element.
20285          * 
20286          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20287          * @param {Event} e The event
20288          * @param {Object} data An object containing arbitrary data supplied by the drag source
20289          */
20290          "out" : true,
20291          
20292         /**
20293          * @event drop
20294          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20295          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20296          * implementation that does something to process the drop event and returns true so that the drag source's
20297          * repair action does not run.
20298          * 
20299          * IMPORTANT : it should set this.success
20300          * 
20301          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20302          * @param {Event} e The event
20303          * @param {Object} data An object containing arbitrary data supplied by the drag source
20304         */
20305          "drop" : true
20306     });
20307             
20308      
20309     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20310         this.el.dom, 
20311         this.ddGroup || this.group,
20312         {
20313             isTarget: true,
20314             listeners : listeners || {} 
20315            
20316         
20317         }
20318     );
20319
20320 };
20321
20322 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20323     /**
20324      * @cfg {String} overClass
20325      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20326      */
20327      /**
20328      * @cfg {String} ddGroup
20329      * The drag drop group to handle drop events for
20330      */
20331      
20332     /**
20333      * @cfg {String} dropAllowed
20334      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20335      */
20336     dropAllowed : "x-dd-drop-ok",
20337     /**
20338      * @cfg {String} dropNotAllowed
20339      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20340      */
20341     dropNotAllowed : "x-dd-drop-nodrop",
20342     /**
20343      * @cfg {boolean} success
20344      * set this after drop listener.. 
20345      */
20346     success : false,
20347     /**
20348      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20349      * if the drop point is valid for over/enter..
20350      */
20351     valid : false,
20352     // private
20353     isTarget : true,
20354
20355     // private
20356     isNotifyTarget : true,
20357     
20358     /**
20359      * @hide
20360      */
20361     notifyEnter : function(dd, e, data)
20362     {
20363         this.valid = true;
20364         this.fireEvent('enter', dd, e, data);
20365         if(this.overClass){
20366             this.el.addClass(this.overClass);
20367         }
20368         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20369             this.valid ? this.dropAllowed : this.dropNotAllowed
20370         );
20371     },
20372
20373     /**
20374      * @hide
20375      */
20376     notifyOver : function(dd, e, data)
20377     {
20378         this.valid = true;
20379         this.fireEvent('over', dd, e, data);
20380         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20381             this.valid ? this.dropAllowed : this.dropNotAllowed
20382         );
20383     },
20384
20385     /**
20386      * @hide
20387      */
20388     notifyOut : function(dd, e, data)
20389     {
20390         this.fireEvent('out', dd, e, data);
20391         if(this.overClass){
20392             this.el.removeClass(this.overClass);
20393         }
20394     },
20395
20396     /**
20397      * @hide
20398      */
20399     notifyDrop : function(dd, e, data)
20400     {
20401         this.success = false;
20402         this.fireEvent('drop', dd, e, data);
20403         return this.success;
20404     }
20405 });/*
20406  * Based on:
20407  * Ext JS Library 1.1.1
20408  * Copyright(c) 2006-2007, Ext JS, LLC.
20409  *
20410  * Originally Released Under LGPL - original licence link has changed is not relivant.
20411  *
20412  * Fork - LGPL
20413  * <script type="text/javascript">
20414  */
20415
20416
20417 /**
20418  * @class Roo.dd.DragZone
20419  * @extends Roo.dd.DragSource
20420  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20421  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20422  * @constructor
20423  * @param {String/HTMLElement/Element} el The container element
20424  * @param {Object} config
20425  */
20426 Roo.dd.DragZone = function(el, config){
20427     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20428     if(this.containerScroll){
20429         Roo.dd.ScrollManager.register(this.el);
20430     }
20431 };
20432
20433 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20434     /**
20435      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20436      * for auto scrolling during drag operations.
20437      */
20438     /**
20439      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20440      * method after a failed drop (defaults to "c3daf9" - light blue)
20441      */
20442
20443     /**
20444      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20445      * for a valid target to drag based on the mouse down. Override this method
20446      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20447      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20448      * @param {EventObject} e The mouse down event
20449      * @return {Object} The dragData
20450      */
20451     getDragData : function(e){
20452         return Roo.dd.Registry.getHandleFromEvent(e);
20453     },
20454     
20455     /**
20456      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20457      * this.dragData.ddel
20458      * @param {Number} x The x position of the click on the dragged object
20459      * @param {Number} y The y position of the click on the dragged object
20460      * @return {Boolean} true to continue the drag, false to cancel
20461      */
20462     onInitDrag : function(x, y){
20463         this.proxy.update(this.dragData.ddel.cloneNode(true));
20464         this.onStartDrag(x, y);
20465         return true;
20466     },
20467     
20468     /**
20469      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20470      */
20471     afterRepair : function(){
20472         if(Roo.enableFx){
20473             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20474         }
20475         this.dragging = false;
20476     },
20477
20478     /**
20479      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20480      * the XY of this.dragData.ddel
20481      * @param {EventObject} e The mouse up event
20482      * @return {Array} The xy location (e.g. [100, 200])
20483      */
20484     getRepairXY : function(e){
20485         return Roo.Element.fly(this.dragData.ddel).getXY();  
20486     }
20487 });/*
20488  * Based on:
20489  * Ext JS Library 1.1.1
20490  * Copyright(c) 2006-2007, Ext JS, LLC.
20491  *
20492  * Originally Released Under LGPL - original licence link has changed is not relivant.
20493  *
20494  * Fork - LGPL
20495  * <script type="text/javascript">
20496  */
20497 /**
20498  * @class Roo.dd.DropZone
20499  * @extends Roo.dd.DropTarget
20500  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20501  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20502  * @constructor
20503  * @param {String/HTMLElement/Element} el The container element
20504  * @param {Object} config
20505  */
20506 Roo.dd.DropZone = function(el, config){
20507     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20508 };
20509
20510 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20511     /**
20512      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20513      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20514      * provide your own custom lookup.
20515      * @param {Event} e The event
20516      * @return {Object} data The custom data
20517      */
20518     getTargetFromEvent : function(e){
20519         return Roo.dd.Registry.getTargetFromEvent(e);
20520     },
20521
20522     /**
20523      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20524      * that it has registered.  This method has no default implementation and should be overridden to provide
20525      * node-specific processing if necessary.
20526      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20527      * {@link #getTargetFromEvent} for this node)
20528      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20529      * @param {Event} e The event
20530      * @param {Object} data An object containing arbitrary data supplied by the drag source
20531      */
20532     onNodeEnter : function(n, dd, e, data){
20533         
20534     },
20535
20536     /**
20537      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20538      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20539      * overridden to provide the proper feedback.
20540      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20541      * {@link #getTargetFromEvent} for this node)
20542      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20543      * @param {Event} e The event
20544      * @param {Object} data An object containing arbitrary data supplied by the drag source
20545      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20546      * underlying {@link Roo.dd.StatusProxy} can be updated
20547      */
20548     onNodeOver : function(n, dd, e, data){
20549         return this.dropAllowed;
20550     },
20551
20552     /**
20553      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20554      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20555      * node-specific processing if necessary.
20556      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20557      * {@link #getTargetFromEvent} for this node)
20558      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20559      * @param {Event} e The event
20560      * @param {Object} data An object containing arbitrary data supplied by the drag source
20561      */
20562     onNodeOut : function(n, dd, e, data){
20563         
20564     },
20565
20566     /**
20567      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20568      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20569      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20570      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20571      * {@link #getTargetFromEvent} for this node)
20572      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20573      * @param {Event} e The event
20574      * @param {Object} data An object containing arbitrary data supplied by the drag source
20575      * @return {Boolean} True if the drop was valid, else false
20576      */
20577     onNodeDrop : function(n, dd, e, data){
20578         return false;
20579     },
20580
20581     /**
20582      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20583      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20584      * it should be overridden to provide the proper feedback if necessary.
20585      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20586      * @param {Event} e The event
20587      * @param {Object} data An object containing arbitrary data supplied by the drag source
20588      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20589      * underlying {@link Roo.dd.StatusProxy} can be updated
20590      */
20591     onContainerOver : function(dd, e, data){
20592         return this.dropNotAllowed;
20593     },
20594
20595     /**
20596      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20597      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20598      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20599      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20600      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20601      * @param {Event} e The event
20602      * @param {Object} data An object containing arbitrary data supplied by the drag source
20603      * @return {Boolean} True if the drop was valid, else false
20604      */
20605     onContainerDrop : function(dd, e, data){
20606         return false;
20607     },
20608
20609     /**
20610      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20611      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20612      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20613      * you should override this method and provide a custom implementation.
20614      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20615      * @param {Event} e The event
20616      * @param {Object} data An object containing arbitrary data supplied by the drag source
20617      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20618      * underlying {@link Roo.dd.StatusProxy} can be updated
20619      */
20620     notifyEnter : function(dd, e, data){
20621         return this.dropNotAllowed;
20622     },
20623
20624     /**
20625      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20626      * This method will be called on every mouse movement while the drag source is over the drop zone.
20627      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20628      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20629      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20630      * registered node, it will call {@link #onContainerOver}.
20631      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20632      * @param {Event} e The event
20633      * @param {Object} data An object containing arbitrary data supplied by the drag source
20634      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20635      * underlying {@link Roo.dd.StatusProxy} can be updated
20636      */
20637     notifyOver : function(dd, e, data){
20638         var n = this.getTargetFromEvent(e);
20639         if(!n){ // not over valid drop target
20640             if(this.lastOverNode){
20641                 this.onNodeOut(this.lastOverNode, dd, e, data);
20642                 this.lastOverNode = null;
20643             }
20644             return this.onContainerOver(dd, e, data);
20645         }
20646         if(this.lastOverNode != n){
20647             if(this.lastOverNode){
20648                 this.onNodeOut(this.lastOverNode, dd, e, data);
20649             }
20650             this.onNodeEnter(n, dd, e, data);
20651             this.lastOverNode = n;
20652         }
20653         return this.onNodeOver(n, dd, e, data);
20654     },
20655
20656     /**
20657      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20658      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20659      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20660      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20661      * @param {Event} e The event
20662      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20663      */
20664     notifyOut : function(dd, e, data){
20665         if(this.lastOverNode){
20666             this.onNodeOut(this.lastOverNode, dd, e, data);
20667             this.lastOverNode = null;
20668         }
20669     },
20670
20671     /**
20672      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20673      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20674      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20675      * otherwise it will call {@link #onContainerDrop}.
20676      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20677      * @param {Event} e The event
20678      * @param {Object} data An object containing arbitrary data supplied by the drag source
20679      * @return {Boolean} True if the drop was valid, else false
20680      */
20681     notifyDrop : function(dd, e, data){
20682         if(this.lastOverNode){
20683             this.onNodeOut(this.lastOverNode, dd, e, data);
20684             this.lastOverNode = null;
20685         }
20686         var n = this.getTargetFromEvent(e);
20687         return n ?
20688             this.onNodeDrop(n, dd, e, data) :
20689             this.onContainerDrop(dd, e, data);
20690     },
20691
20692     // private
20693     triggerCacheRefresh : function(){
20694         Roo.dd.DDM.refreshCache(this.groups);
20695     }  
20696 });/*
20697  * Based on:
20698  * Ext JS Library 1.1.1
20699  * Copyright(c) 2006-2007, Ext JS, LLC.
20700  *
20701  * Originally Released Under LGPL - original licence link has changed is not relivant.
20702  *
20703  * Fork - LGPL
20704  * <script type="text/javascript">
20705  */
20706
20707
20708 /**
20709  * @class Roo.data.SortTypes
20710  * @singleton
20711  * Defines the default sorting (casting?) comparison functions used when sorting data.
20712  */
20713 Roo.data.SortTypes = {
20714     /**
20715      * Default sort that does nothing
20716      * @param {Mixed} s The value being converted
20717      * @return {Mixed} The comparison value
20718      */
20719     none : function(s){
20720         return s;
20721     },
20722     
20723     /**
20724      * The regular expression used to strip tags
20725      * @type {RegExp}
20726      * @property
20727      */
20728     stripTagsRE : /<\/?[^>]+>/gi,
20729     
20730     /**
20731      * Strips all HTML tags to sort on text only
20732      * @param {Mixed} s The value being converted
20733      * @return {String} The comparison value
20734      */
20735     asText : function(s){
20736         return String(s).replace(this.stripTagsRE, "");
20737     },
20738     
20739     /**
20740      * Strips all HTML tags to sort on text only - Case insensitive
20741      * @param {Mixed} s The value being converted
20742      * @return {String} The comparison value
20743      */
20744     asUCText : function(s){
20745         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20746     },
20747     
20748     /**
20749      * Case insensitive string
20750      * @param {Mixed} s The value being converted
20751      * @return {String} The comparison value
20752      */
20753     asUCString : function(s) {
20754         return String(s).toUpperCase();
20755     },
20756     
20757     /**
20758      * Date sorting
20759      * @param {Mixed} s The value being converted
20760      * @return {Number} The comparison value
20761      */
20762     asDate : function(s) {
20763         if(!s){
20764             return 0;
20765         }
20766         if(s instanceof Date){
20767             return s.getTime();
20768         }
20769         return Date.parse(String(s));
20770     },
20771     
20772     /**
20773      * Float sorting
20774      * @param {Mixed} s The value being converted
20775      * @return {Float} The comparison value
20776      */
20777     asFloat : function(s) {
20778         var val = parseFloat(String(s).replace(/,/g, ""));
20779         if(isNaN(val)) val = 0;
20780         return val;
20781     },
20782     
20783     /**
20784      * Integer sorting
20785      * @param {Mixed} s The value being converted
20786      * @return {Number} The comparison value
20787      */
20788     asInt : function(s) {
20789         var val = parseInt(String(s).replace(/,/g, ""));
20790         if(isNaN(val)) val = 0;
20791         return val;
20792     }
20793 };/*
20794  * Based on:
20795  * Ext JS Library 1.1.1
20796  * Copyright(c) 2006-2007, Ext JS, LLC.
20797  *
20798  * Originally Released Under LGPL - original licence link has changed is not relivant.
20799  *
20800  * Fork - LGPL
20801  * <script type="text/javascript">
20802  */
20803
20804 /**
20805 * @class Roo.data.Record
20806  * Instances of this class encapsulate both record <em>definition</em> information, and record
20807  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20808  * to access Records cached in an {@link Roo.data.Store} object.<br>
20809  * <p>
20810  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20811  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20812  * objects.<br>
20813  * <p>
20814  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20815  * @constructor
20816  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20817  * {@link #create}. The parameters are the same.
20818  * @param {Array} data An associative Array of data values keyed by the field name.
20819  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20820  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20821  * not specified an integer id is generated.
20822  */
20823 Roo.data.Record = function(data, id){
20824     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20825     this.data = data;
20826 };
20827
20828 /**
20829  * Generate a constructor for a specific record layout.
20830  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20831  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20832  * Each field definition object may contain the following properties: <ul>
20833  * <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,
20834  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20835  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20836  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20837  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20838  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20839  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20840  * this may be omitted.</p></li>
20841  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20842  * <ul><li>auto (Default, implies no conversion)</li>
20843  * <li>string</li>
20844  * <li>int</li>
20845  * <li>float</li>
20846  * <li>boolean</li>
20847  * <li>date</li></ul></p></li>
20848  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20849  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20850  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20851  * by the Reader into an object that will be stored in the Record. It is passed the
20852  * following parameters:<ul>
20853  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20854  * </ul></p></li>
20855  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20856  * </ul>
20857  * <br>usage:<br><pre><code>
20858 var TopicRecord = Roo.data.Record.create(
20859     {name: 'title', mapping: 'topic_title'},
20860     {name: 'author', mapping: 'username'},
20861     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20862     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20863     {name: 'lastPoster', mapping: 'user2'},
20864     {name: 'excerpt', mapping: 'post_text'}
20865 );
20866
20867 var myNewRecord = new TopicRecord({
20868     title: 'Do my job please',
20869     author: 'noobie',
20870     totalPosts: 1,
20871     lastPost: new Date(),
20872     lastPoster: 'Animal',
20873     excerpt: 'No way dude!'
20874 });
20875 myStore.add(myNewRecord);
20876 </code></pre>
20877  * @method create
20878  * @static
20879  */
20880 Roo.data.Record.create = function(o){
20881     var f = function(){
20882         f.superclass.constructor.apply(this, arguments);
20883     };
20884     Roo.extend(f, Roo.data.Record);
20885     var p = f.prototype;
20886     p.fields = new Roo.util.MixedCollection(false, function(field){
20887         return field.name;
20888     });
20889     for(var i = 0, len = o.length; i < len; i++){
20890         p.fields.add(new Roo.data.Field(o[i]));
20891     }
20892     f.getField = function(name){
20893         return p.fields.get(name);  
20894     };
20895     return f;
20896 };
20897
20898 Roo.data.Record.AUTO_ID = 1000;
20899 Roo.data.Record.EDIT = 'edit';
20900 Roo.data.Record.REJECT = 'reject';
20901 Roo.data.Record.COMMIT = 'commit';
20902
20903 Roo.data.Record.prototype = {
20904     /**
20905      * Readonly flag - true if this record has been modified.
20906      * @type Boolean
20907      */
20908     dirty : false,
20909     editing : false,
20910     error: null,
20911     modified: null,
20912
20913     // private
20914     join : function(store){
20915         this.store = store;
20916     },
20917
20918     /**
20919      * Set the named field to the specified value.
20920      * @param {String} name The name of the field to set.
20921      * @param {Object} value The value to set the field to.
20922      */
20923     set : function(name, value){
20924         if(this.data[name] == value){
20925             return;
20926         }
20927         this.dirty = true;
20928         if(!this.modified){
20929             this.modified = {};
20930         }
20931         if(typeof this.modified[name] == 'undefined'){
20932             this.modified[name] = this.data[name];
20933         }
20934         this.data[name] = value;
20935         if(!this.editing && this.store){
20936             this.store.afterEdit(this);
20937         }       
20938     },
20939
20940     /**
20941      * Get the value of the named field.
20942      * @param {String} name The name of the field to get the value of.
20943      * @return {Object} The value of the field.
20944      */
20945     get : function(name){
20946         return this.data[name]; 
20947     },
20948
20949     // private
20950     beginEdit : function(){
20951         this.editing = true;
20952         this.modified = {}; 
20953     },
20954
20955     // private
20956     cancelEdit : function(){
20957         this.editing = false;
20958         delete this.modified;
20959     },
20960
20961     // private
20962     endEdit : function(){
20963         this.editing = false;
20964         if(this.dirty && this.store){
20965             this.store.afterEdit(this);
20966         }
20967     },
20968
20969     /**
20970      * Usually called by the {@link Roo.data.Store} which owns the Record.
20971      * Rejects all changes made to the Record since either creation, or the last commit operation.
20972      * Modified fields are reverted to their original values.
20973      * <p>
20974      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20975      * of reject operations.
20976      */
20977     reject : function(){
20978         var m = this.modified;
20979         for(var n in m){
20980             if(typeof m[n] != "function"){
20981                 this.data[n] = m[n];
20982             }
20983         }
20984         this.dirty = false;
20985         delete this.modified;
20986         this.editing = false;
20987         if(this.store){
20988             this.store.afterReject(this);
20989         }
20990     },
20991
20992     /**
20993      * Usually called by the {@link Roo.data.Store} which owns the Record.
20994      * Commits all changes made to the Record since either creation, or the last commit operation.
20995      * <p>
20996      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20997      * of commit operations.
20998      */
20999     commit : function(){
21000         this.dirty = false;
21001         delete this.modified;
21002         this.editing = false;
21003         if(this.store){
21004             this.store.afterCommit(this);
21005         }
21006     },
21007
21008     // private
21009     hasError : function(){
21010         return this.error != null;
21011     },
21012
21013     // private
21014     clearError : function(){
21015         this.error = null;
21016     },
21017
21018     /**
21019      * Creates a copy of this record.
21020      * @param {String} id (optional) A new record id if you don't want to use this record's id
21021      * @return {Record}
21022      */
21023     copy : function(newId) {
21024         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21025     }
21026 };/*
21027  * Based on:
21028  * Ext JS Library 1.1.1
21029  * Copyright(c) 2006-2007, Ext JS, LLC.
21030  *
21031  * Originally Released Under LGPL - original licence link has changed is not relivant.
21032  *
21033  * Fork - LGPL
21034  * <script type="text/javascript">
21035  */
21036
21037
21038
21039 /**
21040  * @class Roo.data.Store
21041  * @extends Roo.util.Observable
21042  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21043  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21044  * <p>
21045  * 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
21046  * has no knowledge of the format of the data returned by the Proxy.<br>
21047  * <p>
21048  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21049  * instances from the data object. These records are cached and made available through accessor functions.
21050  * @constructor
21051  * Creates a new Store.
21052  * @param {Object} config A config object containing the objects needed for the Store to access data,
21053  * and read the data into Records.
21054  */
21055 Roo.data.Store = function(config){
21056     this.data = new Roo.util.MixedCollection(false);
21057     this.data.getKey = function(o){
21058         return o.id;
21059     };
21060     this.baseParams = {};
21061     // private
21062     this.paramNames = {
21063         "start" : "start",
21064         "limit" : "limit",
21065         "sort" : "sort",
21066         "dir" : "dir",
21067         "multisort" : "_multisort"
21068     };
21069
21070     if(config && config.data){
21071         this.inlineData = config.data;
21072         delete config.data;
21073     }
21074
21075     Roo.apply(this, config);
21076     
21077     if(this.reader){ // reader passed
21078         this.reader = Roo.factory(this.reader, Roo.data);
21079         this.reader.xmodule = this.xmodule || false;
21080         if(!this.recordType){
21081             this.recordType = this.reader.recordType;
21082         }
21083         if(this.reader.onMetaChange){
21084             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21085         }
21086     }
21087
21088     if(this.recordType){
21089         this.fields = this.recordType.prototype.fields;
21090     }
21091     this.modified = [];
21092
21093     this.addEvents({
21094         /**
21095          * @event datachanged
21096          * Fires when the data cache has changed, and a widget which is using this Store
21097          * as a Record cache should refresh its view.
21098          * @param {Store} this
21099          */
21100         datachanged : true,
21101         /**
21102          * @event metachange
21103          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21104          * @param {Store} this
21105          * @param {Object} meta The JSON metadata
21106          */
21107         metachange : true,
21108         /**
21109          * @event add
21110          * Fires when Records have been added to the Store
21111          * @param {Store} this
21112          * @param {Roo.data.Record[]} records The array of Records added
21113          * @param {Number} index The index at which the record(s) were added
21114          */
21115         add : true,
21116         /**
21117          * @event remove
21118          * Fires when a Record has been removed from the Store
21119          * @param {Store} this
21120          * @param {Roo.data.Record} record The Record that was removed
21121          * @param {Number} index The index at which the record was removed
21122          */
21123         remove : true,
21124         /**
21125          * @event update
21126          * Fires when a Record has been updated
21127          * @param {Store} this
21128          * @param {Roo.data.Record} record The Record that was updated
21129          * @param {String} operation The update operation being performed.  Value may be one of:
21130          * <pre><code>
21131  Roo.data.Record.EDIT
21132  Roo.data.Record.REJECT
21133  Roo.data.Record.COMMIT
21134          * </code></pre>
21135          */
21136         update : true,
21137         /**
21138          * @event clear
21139          * Fires when the data cache has been cleared.
21140          * @param {Store} this
21141          */
21142         clear : true,
21143         /**
21144          * @event beforeload
21145          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21146          * the load action will be canceled.
21147          * @param {Store} this
21148          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21149          */
21150         beforeload : true,
21151         /**
21152          * @event beforeloadadd
21153          * Fires after a new set of Records has been loaded.
21154          * @param {Store} this
21155          * @param {Roo.data.Record[]} records The Records that were loaded
21156          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21157          */
21158         beforeloadadd : true,
21159         /**
21160          * @event load
21161          * Fires after a new set of Records has been loaded, before they are added to the store.
21162          * @param {Store} this
21163          * @param {Roo.data.Record[]} records The Records that were loaded
21164          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21165          * @params {Object} return from reader
21166          */
21167         load : true,
21168         /**
21169          * @event loadexception
21170          * Fires if an exception occurs in the Proxy during loading.
21171          * Called with the signature of the Proxy's "loadexception" event.
21172          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21173          * 
21174          * @param {Proxy} 
21175          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21176          * @param {Object} load options 
21177          * @param {Object} jsonData from your request (normally this contains the Exception)
21178          */
21179         loadexception : true
21180     });
21181     
21182     if(this.proxy){
21183         this.proxy = Roo.factory(this.proxy, Roo.data);
21184         this.proxy.xmodule = this.xmodule || false;
21185         this.relayEvents(this.proxy,  ["loadexception"]);
21186     }
21187     this.sortToggle = {};
21188     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21189
21190     Roo.data.Store.superclass.constructor.call(this);
21191
21192     if(this.inlineData){
21193         this.loadData(this.inlineData);
21194         delete this.inlineData;
21195     }
21196 };
21197
21198 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21199      /**
21200     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21201     * without a remote query - used by combo/forms at present.
21202     */
21203     
21204     /**
21205     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21206     */
21207     /**
21208     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21209     */
21210     /**
21211     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21212     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21213     */
21214     /**
21215     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21216     * on any HTTP request
21217     */
21218     /**
21219     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21220     */
21221     /**
21222     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21223     */
21224     multiSort: false,
21225     /**
21226     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21227     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21228     */
21229     remoteSort : false,
21230
21231     /**
21232     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21233      * loaded or when a record is removed. (defaults to false).
21234     */
21235     pruneModifiedRecords : false,
21236
21237     // private
21238     lastOptions : null,
21239
21240     /**
21241      * Add Records to the Store and fires the add event.
21242      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21243      */
21244     add : function(records){
21245         records = [].concat(records);
21246         for(var i = 0, len = records.length; i < len; i++){
21247             records[i].join(this);
21248         }
21249         var index = this.data.length;
21250         this.data.addAll(records);
21251         this.fireEvent("add", this, records, index);
21252     },
21253
21254     /**
21255      * Remove a Record from the Store and fires the remove event.
21256      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21257      */
21258     remove : function(record){
21259         var index = this.data.indexOf(record);
21260         this.data.removeAt(index);
21261         if(this.pruneModifiedRecords){
21262             this.modified.remove(record);
21263         }
21264         this.fireEvent("remove", this, record, index);
21265     },
21266
21267     /**
21268      * Remove all Records from the Store and fires the clear event.
21269      */
21270     removeAll : function(){
21271         this.data.clear();
21272         if(this.pruneModifiedRecords){
21273             this.modified = [];
21274         }
21275         this.fireEvent("clear", this);
21276     },
21277
21278     /**
21279      * Inserts Records to the Store at the given index and fires the add event.
21280      * @param {Number} index The start index at which to insert the passed Records.
21281      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21282      */
21283     insert : function(index, records){
21284         records = [].concat(records);
21285         for(var i = 0, len = records.length; i < len; i++){
21286             this.data.insert(index, records[i]);
21287             records[i].join(this);
21288         }
21289         this.fireEvent("add", this, records, index);
21290     },
21291
21292     /**
21293      * Get the index within the cache of the passed Record.
21294      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21295      * @return {Number} The index of the passed Record. Returns -1 if not found.
21296      */
21297     indexOf : function(record){
21298         return this.data.indexOf(record);
21299     },
21300
21301     /**
21302      * Get the index within the cache of the Record with the passed id.
21303      * @param {String} id The id of the Record to find.
21304      * @return {Number} The index of the Record. Returns -1 if not found.
21305      */
21306     indexOfId : function(id){
21307         return this.data.indexOfKey(id);
21308     },
21309
21310     /**
21311      * Get the Record with the specified id.
21312      * @param {String} id The id of the Record to find.
21313      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21314      */
21315     getById : function(id){
21316         return this.data.key(id);
21317     },
21318
21319     /**
21320      * Get the Record at the specified index.
21321      * @param {Number} index The index of the Record to find.
21322      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21323      */
21324     getAt : function(index){
21325         return this.data.itemAt(index);
21326     },
21327
21328     /**
21329      * Returns a range of Records between specified indices.
21330      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21331      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21332      * @return {Roo.data.Record[]} An array of Records
21333      */
21334     getRange : function(start, end){
21335         return this.data.getRange(start, end);
21336     },
21337
21338     // private
21339     storeOptions : function(o){
21340         o = Roo.apply({}, o);
21341         delete o.callback;
21342         delete o.scope;
21343         this.lastOptions = o;
21344     },
21345
21346     /**
21347      * Loads the Record cache from the configured Proxy using the configured Reader.
21348      * <p>
21349      * If using remote paging, then the first load call must specify the <em>start</em>
21350      * and <em>limit</em> properties in the options.params property to establish the initial
21351      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21352      * <p>
21353      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21354      * and this call will return before the new data has been loaded. Perform any post-processing
21355      * in a callback function, or in a "load" event handler.</strong>
21356      * <p>
21357      * @param {Object} options An object containing properties which control loading options:<ul>
21358      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21359      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21360      * passed the following arguments:<ul>
21361      * <li>r : Roo.data.Record[]</li>
21362      * <li>options: Options object from the load call</li>
21363      * <li>success: Boolean success indicator</li></ul></li>
21364      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21365      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21366      * </ul>
21367      */
21368     load : function(options){
21369         options = options || {};
21370         if(this.fireEvent("beforeload", this, options) !== false){
21371             this.storeOptions(options);
21372             var p = Roo.apply(options.params || {}, this.baseParams);
21373             // if meta was not loaded from remote source.. try requesting it.
21374             if (!this.reader.metaFromRemote) {
21375                 p._requestMeta = 1;
21376             }
21377             if(this.sortInfo && this.remoteSort){
21378                 var pn = this.paramNames;
21379                 p[pn["sort"]] = this.sortInfo.field;
21380                 p[pn["dir"]] = this.sortInfo.direction;
21381             }
21382             if (this.multiSort) {
21383                 var pn = this.paramNames;
21384                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21385             }
21386             
21387             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21388         }
21389     },
21390
21391     /**
21392      * Reloads the Record cache from the configured Proxy using the configured Reader and
21393      * the options from the last load operation performed.
21394      * @param {Object} options (optional) An object containing properties which may override the options
21395      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21396      * the most recently used options are reused).
21397      */
21398     reload : function(options){
21399         this.load(Roo.applyIf(options||{}, this.lastOptions));
21400     },
21401
21402     // private
21403     // Called as a callback by the Reader during a load operation.
21404     loadRecords : function(o, options, success){
21405         if(!o || success === false){
21406             if(success !== false){
21407                 this.fireEvent("load", this, [], options, o);
21408             }
21409             if(options.callback){
21410                 options.callback.call(options.scope || this, [], options, false);
21411             }
21412             return;
21413         }
21414         // if data returned failure - throw an exception.
21415         if (o.success === false) {
21416             // show a message if no listener is registered.
21417             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21418                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21419             }
21420             // loadmask wil be hooked into this..
21421             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21422             return;
21423         }
21424         var r = o.records, t = o.totalRecords || r.length;
21425         
21426         this.fireEvent("beforeloadadd", this, r, options, o);
21427         
21428         if(!options || options.add !== true){
21429             if(this.pruneModifiedRecords){
21430                 this.modified = [];
21431             }
21432             for(var i = 0, len = r.length; i < len; i++){
21433                 r[i].join(this);
21434             }
21435             if(this.snapshot){
21436                 this.data = this.snapshot;
21437                 delete this.snapshot;
21438             }
21439             this.data.clear();
21440             this.data.addAll(r);
21441             this.totalLength = t;
21442             this.applySort();
21443             this.fireEvent("datachanged", this);
21444         }else{
21445             this.totalLength = Math.max(t, this.data.length+r.length);
21446             this.add(r);
21447         }
21448         this.fireEvent("load", this, r, options, o);
21449         if(options.callback){
21450             options.callback.call(options.scope || this, r, options, true);
21451         }
21452     },
21453
21454
21455     /**
21456      * Loads data from a passed data block. A Reader which understands the format of the data
21457      * must have been configured in the constructor.
21458      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21459      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21460      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21461      */
21462     loadData : function(o, append){
21463         var r = this.reader.readRecords(o);
21464         this.loadRecords(r, {add: append}, true);
21465     },
21466
21467     /**
21468      * Gets the number of cached records.
21469      * <p>
21470      * <em>If using paging, this may not be the total size of the dataset. If the data object
21471      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21472      * the data set size</em>
21473      */
21474     getCount : function(){
21475         return this.data.length || 0;
21476     },
21477
21478     /**
21479      * Gets the total number of records in the dataset as returned by the server.
21480      * <p>
21481      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21482      * the dataset size</em>
21483      */
21484     getTotalCount : function(){
21485         return this.totalLength || 0;
21486     },
21487
21488     /**
21489      * Returns the sort state of the Store as an object with two properties:
21490      * <pre><code>
21491  field {String} The name of the field by which the Records are sorted
21492  direction {String} The sort order, "ASC" or "DESC"
21493      * </code></pre>
21494      */
21495     getSortState : function(){
21496         return this.sortInfo;
21497     },
21498
21499     // private
21500     applySort : function(){
21501         if(this.sortInfo && !this.remoteSort){
21502             var s = this.sortInfo, f = s.field;
21503             var st = this.fields.get(f).sortType;
21504             var fn = function(r1, r2){
21505                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21506                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21507             };
21508             this.data.sort(s.direction, fn);
21509             if(this.snapshot && this.snapshot != this.data){
21510                 this.snapshot.sort(s.direction, fn);
21511             }
21512         }
21513     },
21514
21515     /**
21516      * Sets the default sort column and order to be used by the next load operation.
21517      * @param {String} fieldName The name of the field to sort by.
21518      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21519      */
21520     setDefaultSort : function(field, dir){
21521         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21522     },
21523
21524     /**
21525      * Sort the Records.
21526      * If remote sorting is used, the sort is performed on the server, and the cache is
21527      * reloaded. If local sorting is used, the cache is sorted internally.
21528      * @param {String} fieldName The name of the field to sort by.
21529      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21530      */
21531     sort : function(fieldName, dir){
21532         var f = this.fields.get(fieldName);
21533         if(!dir){
21534             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21535             
21536             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21537                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21538             }else{
21539                 dir = f.sortDir;
21540             }
21541         }
21542         this.sortToggle[f.name] = dir;
21543         this.sortInfo = {field: f.name, direction: dir};
21544         if(!this.remoteSort){
21545             this.applySort();
21546             this.fireEvent("datachanged", this);
21547         }else{
21548             this.load(this.lastOptions);
21549         }
21550     },
21551
21552     /**
21553      * Calls the specified function for each of the Records in the cache.
21554      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21555      * Returning <em>false</em> aborts and exits the iteration.
21556      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21557      */
21558     each : function(fn, scope){
21559         this.data.each(fn, scope);
21560     },
21561
21562     /**
21563      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21564      * (e.g., during paging).
21565      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21566      */
21567     getModifiedRecords : function(){
21568         return this.modified;
21569     },
21570
21571     // private
21572     createFilterFn : function(property, value, anyMatch){
21573         if(!value.exec){ // not a regex
21574             value = String(value);
21575             if(value.length == 0){
21576                 return false;
21577             }
21578             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21579         }
21580         return function(r){
21581             return value.test(r.data[property]);
21582         };
21583     },
21584
21585     /**
21586      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21587      * @param {String} property A field on your records
21588      * @param {Number} start The record index to start at (defaults to 0)
21589      * @param {Number} end The last record index to include (defaults to length - 1)
21590      * @return {Number} The sum
21591      */
21592     sum : function(property, start, end){
21593         var rs = this.data.items, v = 0;
21594         start = start || 0;
21595         end = (end || end === 0) ? end : rs.length-1;
21596
21597         for(var i = start; i <= end; i++){
21598             v += (rs[i].data[property] || 0);
21599         }
21600         return v;
21601     },
21602
21603     /**
21604      * Filter the records by a specified property.
21605      * @param {String} field A field on your records
21606      * @param {String/RegExp} value Either a string that the field
21607      * should start with or a RegExp to test against the field
21608      * @param {Boolean} anyMatch True to match any part not just the beginning
21609      */
21610     filter : function(property, value, anyMatch){
21611         var fn = this.createFilterFn(property, value, anyMatch);
21612         return fn ? this.filterBy(fn) : this.clearFilter();
21613     },
21614
21615     /**
21616      * Filter by a function. The specified function will be called with each
21617      * record in this data source. If the function returns true the record is included,
21618      * otherwise it is filtered.
21619      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21620      * @param {Object} scope (optional) The scope of the function (defaults to this)
21621      */
21622     filterBy : function(fn, scope){
21623         this.snapshot = this.snapshot || this.data;
21624         this.data = this.queryBy(fn, scope||this);
21625         this.fireEvent("datachanged", this);
21626     },
21627
21628     /**
21629      * Query the records by a specified property.
21630      * @param {String} field A field on your records
21631      * @param {String/RegExp} value Either a string that the field
21632      * should start with or a RegExp to test against the field
21633      * @param {Boolean} anyMatch True to match any part not just the beginning
21634      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21635      */
21636     query : function(property, value, anyMatch){
21637         var fn = this.createFilterFn(property, value, anyMatch);
21638         return fn ? this.queryBy(fn) : this.data.clone();
21639     },
21640
21641     /**
21642      * Query by a function. The specified function will be called with each
21643      * record in this data source. If the function returns true the record is included
21644      * in the results.
21645      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21646      * @param {Object} scope (optional) The scope of the function (defaults to this)
21647       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21648      **/
21649     queryBy : function(fn, scope){
21650         var data = this.snapshot || this.data;
21651         return data.filterBy(fn, scope||this);
21652     },
21653
21654     /**
21655      * Collects unique values for a particular dataIndex from this store.
21656      * @param {String} dataIndex The property to collect
21657      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21658      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21659      * @return {Array} An array of the unique values
21660      **/
21661     collect : function(dataIndex, allowNull, bypassFilter){
21662         var d = (bypassFilter === true && this.snapshot) ?
21663                 this.snapshot.items : this.data.items;
21664         var v, sv, r = [], l = {};
21665         for(var i = 0, len = d.length; i < len; i++){
21666             v = d[i].data[dataIndex];
21667             sv = String(v);
21668             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21669                 l[sv] = true;
21670                 r[r.length] = v;
21671             }
21672         }
21673         return r;
21674     },
21675
21676     /**
21677      * Revert to a view of the Record cache with no filtering applied.
21678      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21679      */
21680     clearFilter : function(suppressEvent){
21681         if(this.snapshot && this.snapshot != this.data){
21682             this.data = this.snapshot;
21683             delete this.snapshot;
21684             if(suppressEvent !== true){
21685                 this.fireEvent("datachanged", this);
21686             }
21687         }
21688     },
21689
21690     // private
21691     afterEdit : function(record){
21692         if(this.modified.indexOf(record) == -1){
21693             this.modified.push(record);
21694         }
21695         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21696     },
21697     
21698     // private
21699     afterReject : function(record){
21700         this.modified.remove(record);
21701         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21702     },
21703
21704     // private
21705     afterCommit : function(record){
21706         this.modified.remove(record);
21707         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21708     },
21709
21710     /**
21711      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21712      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21713      */
21714     commitChanges : function(){
21715         var m = this.modified.slice(0);
21716         this.modified = [];
21717         for(var i = 0, len = m.length; i < len; i++){
21718             m[i].commit();
21719         }
21720     },
21721
21722     /**
21723      * Cancel outstanding changes on all changed records.
21724      */
21725     rejectChanges : function(){
21726         var m = this.modified.slice(0);
21727         this.modified = [];
21728         for(var i = 0, len = m.length; i < len; i++){
21729             m[i].reject();
21730         }
21731     },
21732
21733     onMetaChange : function(meta, rtype, o){
21734         this.recordType = rtype;
21735         this.fields = rtype.prototype.fields;
21736         delete this.snapshot;
21737         this.sortInfo = meta.sortInfo || this.sortInfo;
21738         this.modified = [];
21739         this.fireEvent('metachange', this, this.reader.meta);
21740     },
21741     
21742     moveIndex : function(data, type)
21743     {
21744         var index = this.indexOf(data);
21745         
21746         var newIndex = index + type;
21747         
21748         this.remove(data);
21749         
21750         this.insert(newIndex, data);
21751         
21752     }
21753 });/*
21754  * Based on:
21755  * Ext JS Library 1.1.1
21756  * Copyright(c) 2006-2007, Ext JS, LLC.
21757  *
21758  * Originally Released Under LGPL - original licence link has changed is not relivant.
21759  *
21760  * Fork - LGPL
21761  * <script type="text/javascript">
21762  */
21763
21764 /**
21765  * @class Roo.data.SimpleStore
21766  * @extends Roo.data.Store
21767  * Small helper class to make creating Stores from Array data easier.
21768  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21769  * @cfg {Array} fields An array of field definition objects, or field name strings.
21770  * @cfg {Array} data The multi-dimensional array of data
21771  * @constructor
21772  * @param {Object} config
21773  */
21774 Roo.data.SimpleStore = function(config){
21775     Roo.data.SimpleStore.superclass.constructor.call(this, {
21776         isLocal : true,
21777         reader: new Roo.data.ArrayReader({
21778                 id: config.id
21779             },
21780             Roo.data.Record.create(config.fields)
21781         ),
21782         proxy : new Roo.data.MemoryProxy(config.data)
21783     });
21784     this.load();
21785 };
21786 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21787  * Based on:
21788  * Ext JS Library 1.1.1
21789  * Copyright(c) 2006-2007, Ext JS, LLC.
21790  *
21791  * Originally Released Under LGPL - original licence link has changed is not relivant.
21792  *
21793  * Fork - LGPL
21794  * <script type="text/javascript">
21795  */
21796
21797 /**
21798 /**
21799  * @extends Roo.data.Store
21800  * @class Roo.data.JsonStore
21801  * Small helper class to make creating Stores for JSON data easier. <br/>
21802 <pre><code>
21803 var store = new Roo.data.JsonStore({
21804     url: 'get-images.php',
21805     root: 'images',
21806     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21807 });
21808 </code></pre>
21809  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21810  * JsonReader and HttpProxy (unless inline data is provided).</b>
21811  * @cfg {Array} fields An array of field definition objects, or field name strings.
21812  * @constructor
21813  * @param {Object} config
21814  */
21815 Roo.data.JsonStore = function(c){
21816     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21817         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21818         reader: new Roo.data.JsonReader(c, c.fields)
21819     }));
21820 };
21821 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21822  * Based on:
21823  * Ext JS Library 1.1.1
21824  * Copyright(c) 2006-2007, Ext JS, LLC.
21825  *
21826  * Originally Released Under LGPL - original licence link has changed is not relivant.
21827  *
21828  * Fork - LGPL
21829  * <script type="text/javascript">
21830  */
21831
21832  
21833 Roo.data.Field = function(config){
21834     if(typeof config == "string"){
21835         config = {name: config};
21836     }
21837     Roo.apply(this, config);
21838     
21839     if(!this.type){
21840         this.type = "auto";
21841     }
21842     
21843     var st = Roo.data.SortTypes;
21844     // named sortTypes are supported, here we look them up
21845     if(typeof this.sortType == "string"){
21846         this.sortType = st[this.sortType];
21847     }
21848     
21849     // set default sortType for strings and dates
21850     if(!this.sortType){
21851         switch(this.type){
21852             case "string":
21853                 this.sortType = st.asUCString;
21854                 break;
21855             case "date":
21856                 this.sortType = st.asDate;
21857                 break;
21858             default:
21859                 this.sortType = st.none;
21860         }
21861     }
21862
21863     // define once
21864     var stripRe = /[\$,%]/g;
21865
21866     // prebuilt conversion function for this field, instead of
21867     // switching every time we're reading a value
21868     if(!this.convert){
21869         var cv, dateFormat = this.dateFormat;
21870         switch(this.type){
21871             case "":
21872             case "auto":
21873             case undefined:
21874                 cv = function(v){ return v; };
21875                 break;
21876             case "string":
21877                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21878                 break;
21879             case "int":
21880                 cv = function(v){
21881                     return v !== undefined && v !== null && v !== '' ?
21882                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21883                     };
21884                 break;
21885             case "float":
21886                 cv = function(v){
21887                     return v !== undefined && v !== null && v !== '' ?
21888                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21889                     };
21890                 break;
21891             case "bool":
21892             case "boolean":
21893                 cv = function(v){ return v === true || v === "true" || v == 1; };
21894                 break;
21895             case "date":
21896                 cv = function(v){
21897                     if(!v){
21898                         return '';
21899                     }
21900                     if(v instanceof Date){
21901                         return v;
21902                     }
21903                     if(dateFormat){
21904                         if(dateFormat == "timestamp"){
21905                             return new Date(v*1000);
21906                         }
21907                         return Date.parseDate(v, dateFormat);
21908                     }
21909                     var parsed = Date.parse(v);
21910                     return parsed ? new Date(parsed) : null;
21911                 };
21912              break;
21913             
21914         }
21915         this.convert = cv;
21916     }
21917 };
21918
21919 Roo.data.Field.prototype = {
21920     dateFormat: null,
21921     defaultValue: "",
21922     mapping: null,
21923     sortType : null,
21924     sortDir : "ASC"
21925 };/*
21926  * Based on:
21927  * Ext JS Library 1.1.1
21928  * Copyright(c) 2006-2007, Ext JS, LLC.
21929  *
21930  * Originally Released Under LGPL - original licence link has changed is not relivant.
21931  *
21932  * Fork - LGPL
21933  * <script type="text/javascript">
21934  */
21935  
21936 // Base class for reading structured data from a data source.  This class is intended to be
21937 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21938
21939 /**
21940  * @class Roo.data.DataReader
21941  * Base class for reading structured data from a data source.  This class is intended to be
21942  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21943  */
21944
21945 Roo.data.DataReader = function(meta, recordType){
21946     
21947     this.meta = meta;
21948     
21949     this.recordType = recordType instanceof Array ? 
21950         Roo.data.Record.create(recordType) : recordType;
21951 };
21952
21953 Roo.data.DataReader.prototype = {
21954      /**
21955      * Create an empty record
21956      * @param {Object} data (optional) - overlay some values
21957      * @return {Roo.data.Record} record created.
21958      */
21959     newRow :  function(d) {
21960         var da =  {};
21961         this.recordType.prototype.fields.each(function(c) {
21962             switch( c.type) {
21963                 case 'int' : da[c.name] = 0; break;
21964                 case 'date' : da[c.name] = new Date(); break;
21965                 case 'float' : da[c.name] = 0.0; break;
21966                 case 'boolean' : da[c.name] = false; break;
21967                 default : da[c.name] = ""; break;
21968             }
21969             
21970         });
21971         return new this.recordType(Roo.apply(da, d));
21972     }
21973     
21974 };/*
21975  * Based on:
21976  * Ext JS Library 1.1.1
21977  * Copyright(c) 2006-2007, Ext JS, LLC.
21978  *
21979  * Originally Released Under LGPL - original licence link has changed is not relivant.
21980  *
21981  * Fork - LGPL
21982  * <script type="text/javascript">
21983  */
21984
21985 /**
21986  * @class Roo.data.DataProxy
21987  * @extends Roo.data.Observable
21988  * This class is an abstract base class for implementations which provide retrieval of
21989  * unformatted data objects.<br>
21990  * <p>
21991  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21992  * (of the appropriate type which knows how to parse the data object) to provide a block of
21993  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21994  * <p>
21995  * Custom implementations must implement the load method as described in
21996  * {@link Roo.data.HttpProxy#load}.
21997  */
21998 Roo.data.DataProxy = function(){
21999     this.addEvents({
22000         /**
22001          * @event beforeload
22002          * Fires before a network request is made to retrieve a data object.
22003          * @param {Object} This DataProxy object.
22004          * @param {Object} params The params parameter to the load function.
22005          */
22006         beforeload : true,
22007         /**
22008          * @event load
22009          * Fires before the load method's callback is called.
22010          * @param {Object} This DataProxy object.
22011          * @param {Object} o The data object.
22012          * @param {Object} arg The callback argument object passed to the load function.
22013          */
22014         load : true,
22015         /**
22016          * @event loadexception
22017          * Fires if an Exception occurs during data retrieval.
22018          * @param {Object} This DataProxy object.
22019          * @param {Object} o The data object.
22020          * @param {Object} arg The callback argument object passed to the load function.
22021          * @param {Object} e The Exception.
22022          */
22023         loadexception : true
22024     });
22025     Roo.data.DataProxy.superclass.constructor.call(this);
22026 };
22027
22028 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22029
22030     /**
22031      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22032      */
22033 /*
22034  * Based on:
22035  * Ext JS Library 1.1.1
22036  * Copyright(c) 2006-2007, Ext JS, LLC.
22037  *
22038  * Originally Released Under LGPL - original licence link has changed is not relivant.
22039  *
22040  * Fork - LGPL
22041  * <script type="text/javascript">
22042  */
22043 /**
22044  * @class Roo.data.MemoryProxy
22045  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22046  * to the Reader when its load method is called.
22047  * @constructor
22048  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22049  */
22050 Roo.data.MemoryProxy = function(data){
22051     if (data.data) {
22052         data = data.data;
22053     }
22054     Roo.data.MemoryProxy.superclass.constructor.call(this);
22055     this.data = data;
22056 };
22057
22058 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22059     /**
22060      * Load data from the requested source (in this case an in-memory
22061      * data object passed to the constructor), read the data object into
22062      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22063      * process that block using the passed callback.
22064      * @param {Object} params This parameter is not used by the MemoryProxy class.
22065      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22066      * object into a block of Roo.data.Records.
22067      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22068      * The function must be passed <ul>
22069      * <li>The Record block object</li>
22070      * <li>The "arg" argument from the load function</li>
22071      * <li>A boolean success indicator</li>
22072      * </ul>
22073      * @param {Object} scope The scope in which to call the callback
22074      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22075      */
22076     load : function(params, reader, callback, scope, arg){
22077         params = params || {};
22078         var result;
22079         try {
22080             result = reader.readRecords(this.data);
22081         }catch(e){
22082             this.fireEvent("loadexception", this, arg, null, e);
22083             callback.call(scope, null, arg, false);
22084             return;
22085         }
22086         callback.call(scope, result, arg, true);
22087     },
22088     
22089     // private
22090     update : function(params, records){
22091         
22092     }
22093 });/*
22094  * Based on:
22095  * Ext JS Library 1.1.1
22096  * Copyright(c) 2006-2007, Ext JS, LLC.
22097  *
22098  * Originally Released Under LGPL - original licence link has changed is not relivant.
22099  *
22100  * Fork - LGPL
22101  * <script type="text/javascript">
22102  */
22103 /**
22104  * @class Roo.data.HttpProxy
22105  * @extends Roo.data.DataProxy
22106  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22107  * configured to reference a certain URL.<br><br>
22108  * <p>
22109  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22110  * from which the running page was served.<br><br>
22111  * <p>
22112  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22113  * <p>
22114  * Be aware that to enable the browser to parse an XML document, the server must set
22115  * the Content-Type header in the HTTP response to "text/xml".
22116  * @constructor
22117  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22118  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22119  * will be used to make the request.
22120  */
22121 Roo.data.HttpProxy = function(conn){
22122     Roo.data.HttpProxy.superclass.constructor.call(this);
22123     // is conn a conn config or a real conn?
22124     this.conn = conn;
22125     this.useAjax = !conn || !conn.events;
22126   
22127 };
22128
22129 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22130     // thse are take from connection...
22131     
22132     /**
22133      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22134      */
22135     /**
22136      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22137      * extra parameters to each request made by this object. (defaults to undefined)
22138      */
22139     /**
22140      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22141      *  to each request made by this object. (defaults to undefined)
22142      */
22143     /**
22144      * @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)
22145      */
22146     /**
22147      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22148      */
22149      /**
22150      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22151      * @type Boolean
22152      */
22153   
22154
22155     /**
22156      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22157      * @type Boolean
22158      */
22159     /**
22160      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22161      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22162      * a finer-grained basis than the DataProxy events.
22163      */
22164     getConnection : function(){
22165         return this.useAjax ? Roo.Ajax : this.conn;
22166     },
22167
22168     /**
22169      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22170      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22171      * process that block using the passed callback.
22172      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22173      * for the request to the remote server.
22174      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22175      * object into a block of Roo.data.Records.
22176      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22177      * The function must be passed <ul>
22178      * <li>The Record block object</li>
22179      * <li>The "arg" argument from the load function</li>
22180      * <li>A boolean success indicator</li>
22181      * </ul>
22182      * @param {Object} scope The scope in which to call the callback
22183      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22184      */
22185     load : function(params, reader, callback, scope, arg){
22186         if(this.fireEvent("beforeload", this, params) !== false){
22187             var  o = {
22188                 params : params || {},
22189                 request: {
22190                     callback : callback,
22191                     scope : scope,
22192                     arg : arg
22193                 },
22194                 reader: reader,
22195                 callback : this.loadResponse,
22196                 scope: this
22197             };
22198             if(this.useAjax){
22199                 Roo.applyIf(o, this.conn);
22200                 if(this.activeRequest){
22201                     Roo.Ajax.abort(this.activeRequest);
22202                 }
22203                 this.activeRequest = Roo.Ajax.request(o);
22204             }else{
22205                 this.conn.request(o);
22206             }
22207         }else{
22208             callback.call(scope||this, null, arg, false);
22209         }
22210     },
22211
22212     // private
22213     loadResponse : function(o, success, response){
22214         delete this.activeRequest;
22215         if(!success){
22216             this.fireEvent("loadexception", this, o, response);
22217             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22218             return;
22219         }
22220         var result;
22221         try {
22222             result = o.reader.read(response);
22223         }catch(e){
22224             this.fireEvent("loadexception", this, o, response, e);
22225             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22226             return;
22227         }
22228         
22229         this.fireEvent("load", this, o, o.request.arg);
22230         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22231     },
22232
22233     // private
22234     update : function(dataSet){
22235
22236     },
22237
22238     // private
22239     updateResponse : function(dataSet){
22240
22241     }
22242 });/*
22243  * Based on:
22244  * Ext JS Library 1.1.1
22245  * Copyright(c) 2006-2007, Ext JS, LLC.
22246  *
22247  * Originally Released Under LGPL - original licence link has changed is not relivant.
22248  *
22249  * Fork - LGPL
22250  * <script type="text/javascript">
22251  */
22252
22253 /**
22254  * @class Roo.data.ScriptTagProxy
22255  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22256  * other than the originating domain of the running page.<br><br>
22257  * <p>
22258  * <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
22259  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22260  * <p>
22261  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22262  * source code that is used as the source inside a &lt;script> tag.<br><br>
22263  * <p>
22264  * In order for the browser to process the returned data, the server must wrap the data object
22265  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22266  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22267  * depending on whether the callback name was passed:
22268  * <p>
22269  * <pre><code>
22270 boolean scriptTag = false;
22271 String cb = request.getParameter("callback");
22272 if (cb != null) {
22273     scriptTag = true;
22274     response.setContentType("text/javascript");
22275 } else {
22276     response.setContentType("application/x-json");
22277 }
22278 Writer out = response.getWriter();
22279 if (scriptTag) {
22280     out.write(cb + "(");
22281 }
22282 out.print(dataBlock.toJsonString());
22283 if (scriptTag) {
22284     out.write(");");
22285 }
22286 </pre></code>
22287  *
22288  * @constructor
22289  * @param {Object} config A configuration object.
22290  */
22291 Roo.data.ScriptTagProxy = function(config){
22292     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22293     Roo.apply(this, config);
22294     this.head = document.getElementsByTagName("head")[0];
22295 };
22296
22297 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22298
22299 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22300     /**
22301      * @cfg {String} url The URL from which to request the data object.
22302      */
22303     /**
22304      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22305      */
22306     timeout : 30000,
22307     /**
22308      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22309      * the server the name of the callback function set up by the load call to process the returned data object.
22310      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22311      * javascript output which calls this named function passing the data object as its only parameter.
22312      */
22313     callbackParam : "callback",
22314     /**
22315      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22316      * name to the request.
22317      */
22318     nocache : true,
22319
22320     /**
22321      * Load data from the configured URL, read the data object into
22322      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22323      * process that block using the passed callback.
22324      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22325      * for the request to the remote server.
22326      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22327      * object into a block of Roo.data.Records.
22328      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22329      * The function must be passed <ul>
22330      * <li>The Record block object</li>
22331      * <li>The "arg" argument from the load function</li>
22332      * <li>A boolean success indicator</li>
22333      * </ul>
22334      * @param {Object} scope The scope in which to call the callback
22335      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22336      */
22337     load : function(params, reader, callback, scope, arg){
22338         if(this.fireEvent("beforeload", this, params) !== false){
22339
22340             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22341
22342             var url = this.url;
22343             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22344             if(this.nocache){
22345                 url += "&_dc=" + (new Date().getTime());
22346             }
22347             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22348             var trans = {
22349                 id : transId,
22350                 cb : "stcCallback"+transId,
22351                 scriptId : "stcScript"+transId,
22352                 params : params,
22353                 arg : arg,
22354                 url : url,
22355                 callback : callback,
22356                 scope : scope,
22357                 reader : reader
22358             };
22359             var conn = this;
22360
22361             window[trans.cb] = function(o){
22362                 conn.handleResponse(o, trans);
22363             };
22364
22365             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22366
22367             if(this.autoAbort !== false){
22368                 this.abort();
22369             }
22370
22371             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22372
22373             var script = document.createElement("script");
22374             script.setAttribute("src", url);
22375             script.setAttribute("type", "text/javascript");
22376             script.setAttribute("id", trans.scriptId);
22377             this.head.appendChild(script);
22378
22379             this.trans = trans;
22380         }else{
22381             callback.call(scope||this, null, arg, false);
22382         }
22383     },
22384
22385     // private
22386     isLoading : function(){
22387         return this.trans ? true : false;
22388     },
22389
22390     /**
22391      * Abort the current server request.
22392      */
22393     abort : function(){
22394         if(this.isLoading()){
22395             this.destroyTrans(this.trans);
22396         }
22397     },
22398
22399     // private
22400     destroyTrans : function(trans, isLoaded){
22401         this.head.removeChild(document.getElementById(trans.scriptId));
22402         clearTimeout(trans.timeoutId);
22403         if(isLoaded){
22404             window[trans.cb] = undefined;
22405             try{
22406                 delete window[trans.cb];
22407             }catch(e){}
22408         }else{
22409             // if hasn't been loaded, wait for load to remove it to prevent script error
22410             window[trans.cb] = function(){
22411                 window[trans.cb] = undefined;
22412                 try{
22413                     delete window[trans.cb];
22414                 }catch(e){}
22415             };
22416         }
22417     },
22418
22419     // private
22420     handleResponse : function(o, trans){
22421         this.trans = false;
22422         this.destroyTrans(trans, true);
22423         var result;
22424         try {
22425             result = trans.reader.readRecords(o);
22426         }catch(e){
22427             this.fireEvent("loadexception", this, o, trans.arg, e);
22428             trans.callback.call(trans.scope||window, null, trans.arg, false);
22429             return;
22430         }
22431         this.fireEvent("load", this, o, trans.arg);
22432         trans.callback.call(trans.scope||window, result, trans.arg, true);
22433     },
22434
22435     // private
22436     handleFailure : function(trans){
22437         this.trans = false;
22438         this.destroyTrans(trans, false);
22439         this.fireEvent("loadexception", this, null, trans.arg);
22440         trans.callback.call(trans.scope||window, null, trans.arg, false);
22441     }
22442 });/*
22443  * Based on:
22444  * Ext JS Library 1.1.1
22445  * Copyright(c) 2006-2007, Ext JS, LLC.
22446  *
22447  * Originally Released Under LGPL - original licence link has changed is not relivant.
22448  *
22449  * Fork - LGPL
22450  * <script type="text/javascript">
22451  */
22452
22453 /**
22454  * @class Roo.data.JsonReader
22455  * @extends Roo.data.DataReader
22456  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22457  * based on mappings in a provided Roo.data.Record constructor.
22458  * 
22459  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22460  * in the reply previously. 
22461  * 
22462  * <p>
22463  * Example code:
22464  * <pre><code>
22465 var RecordDef = Roo.data.Record.create([
22466     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22467     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22468 ]);
22469 var myReader = new Roo.data.JsonReader({
22470     totalProperty: "results",    // The property which contains the total dataset size (optional)
22471     root: "rows",                // The property which contains an Array of row objects
22472     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22473 }, RecordDef);
22474 </code></pre>
22475  * <p>
22476  * This would consume a JSON file like this:
22477  * <pre><code>
22478 { 'results': 2, 'rows': [
22479     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22480     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22481 }
22482 </code></pre>
22483  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22484  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22485  * paged from the remote server.
22486  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22487  * @cfg {String} root name of the property which contains the Array of row objects.
22488  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22489  * @constructor
22490  * Create a new JsonReader
22491  * @param {Object} meta Metadata configuration options
22492  * @param {Object} recordType Either an Array of field definition objects,
22493  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22494  */
22495 Roo.data.JsonReader = function(meta, recordType){
22496     
22497     meta = meta || {};
22498     // set some defaults:
22499     Roo.applyIf(meta, {
22500         totalProperty: 'total',
22501         successProperty : 'success',
22502         root : 'data',
22503         id : 'id'
22504     });
22505     
22506     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22507 };
22508 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22509     
22510     /**
22511      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22512      * Used by Store query builder to append _requestMeta to params.
22513      * 
22514      */
22515     metaFromRemote : false,
22516     /**
22517      * This method is only used by a DataProxy which has retrieved data from a remote server.
22518      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22519      * @return {Object} data A data block which is used by an Roo.data.Store object as
22520      * a cache of Roo.data.Records.
22521      */
22522     read : function(response){
22523         var json = response.responseText;
22524        
22525         var o = /* eval:var:o */ eval("("+json+")");
22526         if(!o) {
22527             throw {message: "JsonReader.read: Json object not found"};
22528         }
22529         
22530         if(o.metaData){
22531             
22532             delete this.ef;
22533             this.metaFromRemote = true;
22534             this.meta = o.metaData;
22535             this.recordType = Roo.data.Record.create(o.metaData.fields);
22536             this.onMetaChange(this.meta, this.recordType, o);
22537         }
22538         return this.readRecords(o);
22539     },
22540
22541     // private function a store will implement
22542     onMetaChange : function(meta, recordType, o){
22543
22544     },
22545
22546     /**
22547          * @ignore
22548          */
22549     simpleAccess: function(obj, subsc) {
22550         return obj[subsc];
22551     },
22552
22553         /**
22554          * @ignore
22555          */
22556     getJsonAccessor: function(){
22557         var re = /[\[\.]/;
22558         return function(expr) {
22559             try {
22560                 return(re.test(expr))
22561                     ? new Function("obj", "return obj." + expr)
22562                     : function(obj){
22563                         return obj[expr];
22564                     };
22565             } catch(e){}
22566             return Roo.emptyFn;
22567         };
22568     }(),
22569
22570     /**
22571      * Create a data block containing Roo.data.Records from an XML document.
22572      * @param {Object} o An object which contains an Array of row objects in the property specified
22573      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22574      * which contains the total size of the dataset.
22575      * @return {Object} data A data block which is used by an Roo.data.Store object as
22576      * a cache of Roo.data.Records.
22577      */
22578     readRecords : function(o){
22579         /**
22580          * After any data loads, the raw JSON data is available for further custom processing.
22581          * @type Object
22582          */
22583         this.o = o;
22584         var s = this.meta, Record = this.recordType,
22585             f = Record.prototype.fields, fi = f.items, fl = f.length;
22586
22587 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22588         if (!this.ef) {
22589             if(s.totalProperty) {
22590                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22591                 }
22592                 if(s.successProperty) {
22593                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22594                 }
22595                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22596                 if (s.id) {
22597                         var g = this.getJsonAccessor(s.id);
22598                         this.getId = function(rec) {
22599                                 var r = g(rec);
22600                                 return (r === undefined || r === "") ? null : r;
22601                         };
22602                 } else {
22603                         this.getId = function(){return null;};
22604                 }
22605             this.ef = [];
22606             for(var jj = 0; jj < fl; jj++){
22607                 f = fi[jj];
22608                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22609                 this.ef[jj] = this.getJsonAccessor(map);
22610             }
22611         }
22612
22613         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22614         if(s.totalProperty){
22615             var vt = parseInt(this.getTotal(o), 10);
22616             if(!isNaN(vt)){
22617                 totalRecords = vt;
22618             }
22619         }
22620         if(s.successProperty){
22621             var vs = this.getSuccess(o);
22622             if(vs === false || vs === 'false'){
22623                 success = false;
22624             }
22625         }
22626         var records = [];
22627             for(var i = 0; i < c; i++){
22628                     var n = root[i];
22629                 var values = {};
22630                 var id = this.getId(n);
22631                 for(var j = 0; j < fl; j++){
22632                     f = fi[j];
22633                 var v = this.ef[j](n);
22634                 if (!f.convert) {
22635                     Roo.log('missing convert for ' + f.name);
22636                     Roo.log(f);
22637                     continue;
22638                 }
22639                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22640                 }
22641                 var record = new Record(values, id);
22642                 record.json = n;
22643                 records[i] = record;
22644             }
22645             return {
22646             raw : o,
22647                 success : success,
22648                 records : records,
22649                 totalRecords : totalRecords
22650             };
22651     }
22652 });/*
22653  * Based on:
22654  * Ext JS Library 1.1.1
22655  * Copyright(c) 2006-2007, Ext JS, LLC.
22656  *
22657  * Originally Released Under LGPL - original licence link has changed is not relivant.
22658  *
22659  * Fork - LGPL
22660  * <script type="text/javascript">
22661  */
22662
22663 /**
22664  * @class Roo.data.XmlReader
22665  * @extends Roo.data.DataReader
22666  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22667  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22668  * <p>
22669  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22670  * header in the HTTP response must be set to "text/xml".</em>
22671  * <p>
22672  * Example code:
22673  * <pre><code>
22674 var RecordDef = Roo.data.Record.create([
22675    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22676    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22677 ]);
22678 var myReader = new Roo.data.XmlReader({
22679    totalRecords: "results", // The element which contains the total dataset size (optional)
22680    record: "row",           // The repeated element which contains row information
22681    id: "id"                 // The element within the row that provides an ID for the record (optional)
22682 }, RecordDef);
22683 </code></pre>
22684  * <p>
22685  * This would consume an XML file like this:
22686  * <pre><code>
22687 &lt;?xml?>
22688 &lt;dataset>
22689  &lt;results>2&lt;/results>
22690  &lt;row>
22691    &lt;id>1&lt;/id>
22692    &lt;name>Bill&lt;/name>
22693    &lt;occupation>Gardener&lt;/occupation>
22694  &lt;/row>
22695  &lt;row>
22696    &lt;id>2&lt;/id>
22697    &lt;name>Ben&lt;/name>
22698    &lt;occupation>Horticulturalist&lt;/occupation>
22699  &lt;/row>
22700 &lt;/dataset>
22701 </code></pre>
22702  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22703  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22704  * paged from the remote server.
22705  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22706  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22707  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22708  * a record identifier value.
22709  * @constructor
22710  * Create a new XmlReader
22711  * @param {Object} meta Metadata configuration options
22712  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22713  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22714  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22715  */
22716 Roo.data.XmlReader = function(meta, recordType){
22717     meta = meta || {};
22718     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22719 };
22720 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22721     /**
22722      * This method is only used by a DataProxy which has retrieved data from a remote server.
22723          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22724          * to contain a method called 'responseXML' that returns an XML document object.
22725      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22726      * a cache of Roo.data.Records.
22727      */
22728     read : function(response){
22729         var doc = response.responseXML;
22730         if(!doc) {
22731             throw {message: "XmlReader.read: XML Document not available"};
22732         }
22733         return this.readRecords(doc);
22734     },
22735
22736     /**
22737      * Create a data block containing Roo.data.Records from an XML document.
22738          * @param {Object} doc A parsed XML document.
22739      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22740      * a cache of Roo.data.Records.
22741      */
22742     readRecords : function(doc){
22743         /**
22744          * After any data loads/reads, the raw XML Document is available for further custom processing.
22745          * @type XMLDocument
22746          */
22747         this.xmlData = doc;
22748         var root = doc.documentElement || doc;
22749         var q = Roo.DomQuery;
22750         var recordType = this.recordType, fields = recordType.prototype.fields;
22751         var sid = this.meta.id;
22752         var totalRecords = 0, success = true;
22753         if(this.meta.totalRecords){
22754             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22755         }
22756         
22757         if(this.meta.success){
22758             var sv = q.selectValue(this.meta.success, root, true);
22759             success = sv !== false && sv !== 'false';
22760         }
22761         var records = [];
22762         var ns = q.select(this.meta.record, root);
22763         for(var i = 0, len = ns.length; i < len; i++) {
22764                 var n = ns[i];
22765                 var values = {};
22766                 var id = sid ? q.selectValue(sid, n) : undefined;
22767                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22768                     var f = fields.items[j];
22769                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22770                     v = f.convert(v);
22771                     values[f.name] = v;
22772                 }
22773                 var record = new recordType(values, id);
22774                 record.node = n;
22775                 records[records.length] = record;
22776             }
22777
22778             return {
22779                 success : success,
22780                 records : records,
22781                 totalRecords : totalRecords || records.length
22782             };
22783     }
22784 });/*
22785  * Based on:
22786  * Ext JS Library 1.1.1
22787  * Copyright(c) 2006-2007, Ext JS, LLC.
22788  *
22789  * Originally Released Under LGPL - original licence link has changed is not relivant.
22790  *
22791  * Fork - LGPL
22792  * <script type="text/javascript">
22793  */
22794
22795 /**
22796  * @class Roo.data.ArrayReader
22797  * @extends Roo.data.DataReader
22798  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22799  * Each element of that Array represents a row of data fields. The
22800  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22801  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22802  * <p>
22803  * Example code:.
22804  * <pre><code>
22805 var RecordDef = Roo.data.Record.create([
22806     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22807     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22808 ]);
22809 var myReader = new Roo.data.ArrayReader({
22810     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22811 }, RecordDef);
22812 </code></pre>
22813  * <p>
22814  * This would consume an Array like this:
22815  * <pre><code>
22816 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22817   </code></pre>
22818  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22819  * @constructor
22820  * Create a new JsonReader
22821  * @param {Object} meta Metadata configuration options.
22822  * @param {Object} recordType Either an Array of field definition objects
22823  * as specified to {@link Roo.data.Record#create},
22824  * or an {@link Roo.data.Record} object
22825  * created using {@link Roo.data.Record#create}.
22826  */
22827 Roo.data.ArrayReader = function(meta, recordType){
22828     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22829 };
22830
22831 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22832     /**
22833      * Create a data block containing Roo.data.Records from an XML document.
22834      * @param {Object} o An Array of row objects which represents the dataset.
22835      * @return {Object} data A data block which is used by an Roo.data.Store object as
22836      * a cache of Roo.data.Records.
22837      */
22838     readRecords : function(o){
22839         var sid = this.meta ? this.meta.id : null;
22840         var recordType = this.recordType, fields = recordType.prototype.fields;
22841         var records = [];
22842         var root = o;
22843             for(var i = 0; i < root.length; i++){
22844                     var n = root[i];
22845                 var values = {};
22846                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22847                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22848                 var f = fields.items[j];
22849                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22850                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22851                 v = f.convert(v);
22852                 values[f.name] = v;
22853             }
22854                 var record = new recordType(values, id);
22855                 record.json = n;
22856                 records[records.length] = record;
22857             }
22858             return {
22859                 records : records,
22860                 totalRecords : records.length
22861             };
22862     }
22863 });/*
22864  * Based on:
22865  * Ext JS Library 1.1.1
22866  * Copyright(c) 2006-2007, Ext JS, LLC.
22867  *
22868  * Originally Released Under LGPL - original licence link has changed is not relivant.
22869  *
22870  * Fork - LGPL
22871  * <script type="text/javascript">
22872  */
22873
22874
22875 /**
22876  * @class Roo.data.Tree
22877  * @extends Roo.util.Observable
22878  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22879  * in the tree have most standard DOM functionality.
22880  * @constructor
22881  * @param {Node} root (optional) The root node
22882  */
22883 Roo.data.Tree = function(root){
22884    this.nodeHash = {};
22885    /**
22886     * The root node for this tree
22887     * @type Node
22888     */
22889    this.root = null;
22890    if(root){
22891        this.setRootNode(root);
22892    }
22893    this.addEvents({
22894        /**
22895         * @event append
22896         * Fires when a new child node is appended to a node in this tree.
22897         * @param {Tree} tree The owner tree
22898         * @param {Node} parent The parent node
22899         * @param {Node} node The newly appended node
22900         * @param {Number} index The index of the newly appended node
22901         */
22902        "append" : true,
22903        /**
22904         * @event remove
22905         * Fires when a child node is removed from a node in this tree.
22906         * @param {Tree} tree The owner tree
22907         * @param {Node} parent The parent node
22908         * @param {Node} node The child node removed
22909         */
22910        "remove" : true,
22911        /**
22912         * @event move
22913         * Fires when a node is moved to a new location in the tree
22914         * @param {Tree} tree The owner tree
22915         * @param {Node} node The node moved
22916         * @param {Node} oldParent The old parent of this node
22917         * @param {Node} newParent The new parent of this node
22918         * @param {Number} index The index it was moved to
22919         */
22920        "move" : true,
22921        /**
22922         * @event insert
22923         * Fires when a new child node is inserted in a node in this tree.
22924         * @param {Tree} tree The owner tree
22925         * @param {Node} parent The parent node
22926         * @param {Node} node The child node inserted
22927         * @param {Node} refNode The child node the node was inserted before
22928         */
22929        "insert" : true,
22930        /**
22931         * @event beforeappend
22932         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22933         * @param {Tree} tree The owner tree
22934         * @param {Node} parent The parent node
22935         * @param {Node} node The child node to be appended
22936         */
22937        "beforeappend" : true,
22938        /**
22939         * @event beforeremove
22940         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22941         * @param {Tree} tree The owner tree
22942         * @param {Node} parent The parent node
22943         * @param {Node} node The child node to be removed
22944         */
22945        "beforeremove" : true,
22946        /**
22947         * @event beforemove
22948         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22949         * @param {Tree} tree The owner tree
22950         * @param {Node} node The node being moved
22951         * @param {Node} oldParent The parent of the node
22952         * @param {Node} newParent The new parent the node is moving to
22953         * @param {Number} index The index it is being moved to
22954         */
22955        "beforemove" : true,
22956        /**
22957         * @event beforeinsert
22958         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22959         * @param {Tree} tree The owner tree
22960         * @param {Node} parent The parent node
22961         * @param {Node} node The child node to be inserted
22962         * @param {Node} refNode The child node the node is being inserted before
22963         */
22964        "beforeinsert" : true
22965    });
22966
22967     Roo.data.Tree.superclass.constructor.call(this);
22968 };
22969
22970 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22971     pathSeparator: "/",
22972
22973     proxyNodeEvent : function(){
22974         return this.fireEvent.apply(this, arguments);
22975     },
22976
22977     /**
22978      * Returns the root node for this tree.
22979      * @return {Node}
22980      */
22981     getRootNode : function(){
22982         return this.root;
22983     },
22984
22985     /**
22986      * Sets the root node for this tree.
22987      * @param {Node} node
22988      * @return {Node}
22989      */
22990     setRootNode : function(node){
22991         this.root = node;
22992         node.ownerTree = this;
22993         node.isRoot = true;
22994         this.registerNode(node);
22995         return node;
22996     },
22997
22998     /**
22999      * Gets a node in this tree by its id.
23000      * @param {String} id
23001      * @return {Node}
23002      */
23003     getNodeById : function(id){
23004         return this.nodeHash[id];
23005     },
23006
23007     registerNode : function(node){
23008         this.nodeHash[node.id] = node;
23009     },
23010
23011     unregisterNode : function(node){
23012         delete this.nodeHash[node.id];
23013     },
23014
23015     toString : function(){
23016         return "[Tree"+(this.id?" "+this.id:"")+"]";
23017     }
23018 });
23019
23020 /**
23021  * @class Roo.data.Node
23022  * @extends Roo.util.Observable
23023  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23024  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23025  * @constructor
23026  * @param {Object} attributes The attributes/config for the node
23027  */
23028 Roo.data.Node = function(attributes){
23029     /**
23030      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23031      * @type {Object}
23032      */
23033     this.attributes = attributes || {};
23034     this.leaf = this.attributes.leaf;
23035     /**
23036      * The node id. @type String
23037      */
23038     this.id = this.attributes.id;
23039     if(!this.id){
23040         this.id = Roo.id(null, "ynode-");
23041         this.attributes.id = this.id;
23042     }
23043      
23044     
23045     /**
23046      * All child nodes of this node. @type Array
23047      */
23048     this.childNodes = [];
23049     if(!this.childNodes.indexOf){ // indexOf is a must
23050         this.childNodes.indexOf = function(o){
23051             for(var i = 0, len = this.length; i < len; i++){
23052                 if(this[i] == o) {
23053                     return i;
23054                 }
23055             }
23056             return -1;
23057         };
23058     }
23059     /**
23060      * The parent node for this node. @type Node
23061      */
23062     this.parentNode = null;
23063     /**
23064      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23065      */
23066     this.firstChild = null;
23067     /**
23068      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23069      */
23070     this.lastChild = null;
23071     /**
23072      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23073      */
23074     this.previousSibling = null;
23075     /**
23076      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23077      */
23078     this.nextSibling = null;
23079
23080     this.addEvents({
23081        /**
23082         * @event append
23083         * Fires when a new child node is appended
23084         * @param {Tree} tree The owner tree
23085         * @param {Node} this This node
23086         * @param {Node} node The newly appended node
23087         * @param {Number} index The index of the newly appended node
23088         */
23089        "append" : true,
23090        /**
23091         * @event remove
23092         * Fires when a child node is removed
23093         * @param {Tree} tree The owner tree
23094         * @param {Node} this This node
23095         * @param {Node} node The removed node
23096         */
23097        "remove" : true,
23098        /**
23099         * @event move
23100         * Fires when this node is moved to a new location in the tree
23101         * @param {Tree} tree The owner tree
23102         * @param {Node} this This node
23103         * @param {Node} oldParent The old parent of this node
23104         * @param {Node} newParent The new parent of this node
23105         * @param {Number} index The index it was moved to
23106         */
23107        "move" : true,
23108        /**
23109         * @event insert
23110         * Fires when a new child node is inserted.
23111         * @param {Tree} tree The owner tree
23112         * @param {Node} this This node
23113         * @param {Node} node The child node inserted
23114         * @param {Node} refNode The child node the node was inserted before
23115         */
23116        "insert" : true,
23117        /**
23118         * @event beforeappend
23119         * Fires before a new child is appended, return false to cancel the append.
23120         * @param {Tree} tree The owner tree
23121         * @param {Node} this This node
23122         * @param {Node} node The child node to be appended
23123         */
23124        "beforeappend" : true,
23125        /**
23126         * @event beforeremove
23127         * Fires before a child is removed, return false to cancel the remove.
23128         * @param {Tree} tree The owner tree
23129         * @param {Node} this This node
23130         * @param {Node} node The child node to be removed
23131         */
23132        "beforeremove" : true,
23133        /**
23134         * @event beforemove
23135         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23136         * @param {Tree} tree The owner tree
23137         * @param {Node} this This node
23138         * @param {Node} oldParent The parent of this node
23139         * @param {Node} newParent The new parent this node is moving to
23140         * @param {Number} index The index it is being moved to
23141         */
23142        "beforemove" : true,
23143        /**
23144         * @event beforeinsert
23145         * Fires before a new child is inserted, return false to cancel the insert.
23146         * @param {Tree} tree The owner tree
23147         * @param {Node} this This node
23148         * @param {Node} node The child node to be inserted
23149         * @param {Node} refNode The child node the node is being inserted before
23150         */
23151        "beforeinsert" : true
23152    });
23153     this.listeners = this.attributes.listeners;
23154     Roo.data.Node.superclass.constructor.call(this);
23155 };
23156
23157 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23158     fireEvent : function(evtName){
23159         // first do standard event for this node
23160         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23161             return false;
23162         }
23163         // then bubble it up to the tree if the event wasn't cancelled
23164         var ot = this.getOwnerTree();
23165         if(ot){
23166             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23167                 return false;
23168             }
23169         }
23170         return true;
23171     },
23172
23173     /**
23174      * Returns true if this node is a leaf
23175      * @return {Boolean}
23176      */
23177     isLeaf : function(){
23178         return this.leaf === true;
23179     },
23180
23181     // private
23182     setFirstChild : function(node){
23183         this.firstChild = node;
23184     },
23185
23186     //private
23187     setLastChild : function(node){
23188         this.lastChild = node;
23189     },
23190
23191
23192     /**
23193      * Returns true if this node is the last child of its parent
23194      * @return {Boolean}
23195      */
23196     isLast : function(){
23197        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23198     },
23199
23200     /**
23201      * Returns true if this node is the first child of its parent
23202      * @return {Boolean}
23203      */
23204     isFirst : function(){
23205        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23206     },
23207
23208     hasChildNodes : function(){
23209         return !this.isLeaf() && this.childNodes.length > 0;
23210     },
23211
23212     /**
23213      * Insert node(s) as the last child node of this node.
23214      * @param {Node/Array} node The node or Array of nodes to append
23215      * @return {Node} The appended node if single append, or null if an array was passed
23216      */
23217     appendChild : function(node){
23218         var multi = false;
23219         if(node instanceof Array){
23220             multi = node;
23221         }else if(arguments.length > 1){
23222             multi = arguments;
23223         }
23224         // if passed an array or multiple args do them one by one
23225         if(multi){
23226             for(var i = 0, len = multi.length; i < len; i++) {
23227                 this.appendChild(multi[i]);
23228             }
23229         }else{
23230             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23231                 return false;
23232             }
23233             var index = this.childNodes.length;
23234             var oldParent = node.parentNode;
23235             // it's a move, make sure we move it cleanly
23236             if(oldParent){
23237                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23238                     return false;
23239                 }
23240                 oldParent.removeChild(node);
23241             }
23242             index = this.childNodes.length;
23243             if(index == 0){
23244                 this.setFirstChild(node);
23245             }
23246             this.childNodes.push(node);
23247             node.parentNode = this;
23248             var ps = this.childNodes[index-1];
23249             if(ps){
23250                 node.previousSibling = ps;
23251                 ps.nextSibling = node;
23252             }else{
23253                 node.previousSibling = null;
23254             }
23255             node.nextSibling = null;
23256             this.setLastChild(node);
23257             node.setOwnerTree(this.getOwnerTree());
23258             this.fireEvent("append", this.ownerTree, this, node, index);
23259             if(oldParent){
23260                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23261             }
23262             return node;
23263         }
23264     },
23265
23266     /**
23267      * Removes a child node from this node.
23268      * @param {Node} node The node to remove
23269      * @return {Node} The removed node
23270      */
23271     removeChild : function(node){
23272         var index = this.childNodes.indexOf(node);
23273         if(index == -1){
23274             return false;
23275         }
23276         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23277             return false;
23278         }
23279
23280         // remove it from childNodes collection
23281         this.childNodes.splice(index, 1);
23282
23283         // update siblings
23284         if(node.previousSibling){
23285             node.previousSibling.nextSibling = node.nextSibling;
23286         }
23287         if(node.nextSibling){
23288             node.nextSibling.previousSibling = node.previousSibling;
23289         }
23290
23291         // update child refs
23292         if(this.firstChild == node){
23293             this.setFirstChild(node.nextSibling);
23294         }
23295         if(this.lastChild == node){
23296             this.setLastChild(node.previousSibling);
23297         }
23298
23299         node.setOwnerTree(null);
23300         // clear any references from the node
23301         node.parentNode = null;
23302         node.previousSibling = null;
23303         node.nextSibling = null;
23304         this.fireEvent("remove", this.ownerTree, this, node);
23305         return node;
23306     },
23307
23308     /**
23309      * Inserts the first node before the second node in this nodes childNodes collection.
23310      * @param {Node} node The node to insert
23311      * @param {Node} refNode The node to insert before (if null the node is appended)
23312      * @return {Node} The inserted node
23313      */
23314     insertBefore : function(node, refNode){
23315         if(!refNode){ // like standard Dom, refNode can be null for append
23316             return this.appendChild(node);
23317         }
23318         // nothing to do
23319         if(node == refNode){
23320             return false;
23321         }
23322
23323         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23324             return false;
23325         }
23326         var index = this.childNodes.indexOf(refNode);
23327         var oldParent = node.parentNode;
23328         var refIndex = index;
23329
23330         // when moving internally, indexes will change after remove
23331         if(oldParent == this && this.childNodes.indexOf(node) < index){
23332             refIndex--;
23333         }
23334
23335         // it's a move, make sure we move it cleanly
23336         if(oldParent){
23337             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23338                 return false;
23339             }
23340             oldParent.removeChild(node);
23341         }
23342         if(refIndex == 0){
23343             this.setFirstChild(node);
23344         }
23345         this.childNodes.splice(refIndex, 0, node);
23346         node.parentNode = this;
23347         var ps = this.childNodes[refIndex-1];
23348         if(ps){
23349             node.previousSibling = ps;
23350             ps.nextSibling = node;
23351         }else{
23352             node.previousSibling = null;
23353         }
23354         node.nextSibling = refNode;
23355         refNode.previousSibling = node;
23356         node.setOwnerTree(this.getOwnerTree());
23357         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23358         if(oldParent){
23359             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23360         }
23361         return node;
23362     },
23363
23364     /**
23365      * Returns the child node at the specified index.
23366      * @param {Number} index
23367      * @return {Node}
23368      */
23369     item : function(index){
23370         return this.childNodes[index];
23371     },
23372
23373     /**
23374      * Replaces one child node in this node with another.
23375      * @param {Node} newChild The replacement node
23376      * @param {Node} oldChild The node to replace
23377      * @return {Node} The replaced node
23378      */
23379     replaceChild : function(newChild, oldChild){
23380         this.insertBefore(newChild, oldChild);
23381         this.removeChild(oldChild);
23382         return oldChild;
23383     },
23384
23385     /**
23386      * Returns the index of a child node
23387      * @param {Node} node
23388      * @return {Number} The index of the node or -1 if it was not found
23389      */
23390     indexOf : function(child){
23391         return this.childNodes.indexOf(child);
23392     },
23393
23394     /**
23395      * Returns the tree this node is in.
23396      * @return {Tree}
23397      */
23398     getOwnerTree : function(){
23399         // if it doesn't have one, look for one
23400         if(!this.ownerTree){
23401             var p = this;
23402             while(p){
23403                 if(p.ownerTree){
23404                     this.ownerTree = p.ownerTree;
23405                     break;
23406                 }
23407                 p = p.parentNode;
23408             }
23409         }
23410         return this.ownerTree;
23411     },
23412
23413     /**
23414      * Returns depth of this node (the root node has a depth of 0)
23415      * @return {Number}
23416      */
23417     getDepth : function(){
23418         var depth = 0;
23419         var p = this;
23420         while(p.parentNode){
23421             ++depth;
23422             p = p.parentNode;
23423         }
23424         return depth;
23425     },
23426
23427     // private
23428     setOwnerTree : function(tree){
23429         // if it's move, we need to update everyone
23430         if(tree != this.ownerTree){
23431             if(this.ownerTree){
23432                 this.ownerTree.unregisterNode(this);
23433             }
23434             this.ownerTree = tree;
23435             var cs = this.childNodes;
23436             for(var i = 0, len = cs.length; i < len; i++) {
23437                 cs[i].setOwnerTree(tree);
23438             }
23439             if(tree){
23440                 tree.registerNode(this);
23441             }
23442         }
23443     },
23444
23445     /**
23446      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23447      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23448      * @return {String} The path
23449      */
23450     getPath : function(attr){
23451         attr = attr || "id";
23452         var p = this.parentNode;
23453         var b = [this.attributes[attr]];
23454         while(p){
23455             b.unshift(p.attributes[attr]);
23456             p = p.parentNode;
23457         }
23458         var sep = this.getOwnerTree().pathSeparator;
23459         return sep + b.join(sep);
23460     },
23461
23462     /**
23463      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23464      * function call will be the scope provided or the current node. The arguments to the function
23465      * will be the args provided or the current node. If the function returns false at any point,
23466      * the bubble is stopped.
23467      * @param {Function} fn The function to call
23468      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23469      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23470      */
23471     bubble : function(fn, scope, args){
23472         var p = this;
23473         while(p){
23474             if(fn.call(scope || p, args || p) === false){
23475                 break;
23476             }
23477             p = p.parentNode;
23478         }
23479     },
23480
23481     /**
23482      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23483      * function call will be the scope provided or the current node. The arguments to the function
23484      * will be the args provided or the current node. If the function returns false at any point,
23485      * the cascade is stopped on that branch.
23486      * @param {Function} fn The function to call
23487      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23488      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23489      */
23490     cascade : function(fn, scope, args){
23491         if(fn.call(scope || this, args || this) !== false){
23492             var cs = this.childNodes;
23493             for(var i = 0, len = cs.length; i < len; i++) {
23494                 cs[i].cascade(fn, scope, args);
23495             }
23496         }
23497     },
23498
23499     /**
23500      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23501      * function call will be the scope provided or the current node. The arguments to the function
23502      * will be the args provided or the current node. If the function returns false at any point,
23503      * the iteration stops.
23504      * @param {Function} fn The function to call
23505      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23506      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23507      */
23508     eachChild : function(fn, scope, args){
23509         var cs = this.childNodes;
23510         for(var i = 0, len = cs.length; i < len; i++) {
23511                 if(fn.call(scope || this, args || cs[i]) === false){
23512                     break;
23513                 }
23514         }
23515     },
23516
23517     /**
23518      * Finds the first child that has the attribute with the specified value.
23519      * @param {String} attribute The attribute name
23520      * @param {Mixed} value The value to search for
23521      * @return {Node} The found child or null if none was found
23522      */
23523     findChild : function(attribute, value){
23524         var cs = this.childNodes;
23525         for(var i = 0, len = cs.length; i < len; i++) {
23526                 if(cs[i].attributes[attribute] == value){
23527                     return cs[i];
23528                 }
23529         }
23530         return null;
23531     },
23532
23533     /**
23534      * Finds the first child by a custom function. The child matches if the function passed
23535      * returns true.
23536      * @param {Function} fn
23537      * @param {Object} scope (optional)
23538      * @return {Node} The found child or null if none was found
23539      */
23540     findChildBy : function(fn, scope){
23541         var cs = this.childNodes;
23542         for(var i = 0, len = cs.length; i < len; i++) {
23543                 if(fn.call(scope||cs[i], cs[i]) === true){
23544                     return cs[i];
23545                 }
23546         }
23547         return null;
23548     },
23549
23550     /**
23551      * Sorts this nodes children using the supplied sort function
23552      * @param {Function} fn
23553      * @param {Object} scope (optional)
23554      */
23555     sort : function(fn, scope){
23556         var cs = this.childNodes;
23557         var len = cs.length;
23558         if(len > 0){
23559             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23560             cs.sort(sortFn);
23561             for(var i = 0; i < len; i++){
23562                 var n = cs[i];
23563                 n.previousSibling = cs[i-1];
23564                 n.nextSibling = cs[i+1];
23565                 if(i == 0){
23566                     this.setFirstChild(n);
23567                 }
23568                 if(i == len-1){
23569                     this.setLastChild(n);
23570                 }
23571             }
23572         }
23573     },
23574
23575     /**
23576      * Returns true if this node is an ancestor (at any point) of the passed node.
23577      * @param {Node} node
23578      * @return {Boolean}
23579      */
23580     contains : function(node){
23581         return node.isAncestor(this);
23582     },
23583
23584     /**
23585      * Returns true if the passed node is an ancestor (at any point) of this node.
23586      * @param {Node} node
23587      * @return {Boolean}
23588      */
23589     isAncestor : function(node){
23590         var p = this.parentNode;
23591         while(p){
23592             if(p == node){
23593                 return true;
23594             }
23595             p = p.parentNode;
23596         }
23597         return false;
23598     },
23599
23600     toString : function(){
23601         return "[Node"+(this.id?" "+this.id:"")+"]";
23602     }
23603 });/*
23604  * Based on:
23605  * Ext JS Library 1.1.1
23606  * Copyright(c) 2006-2007, Ext JS, LLC.
23607  *
23608  * Originally Released Under LGPL - original licence link has changed is not relivant.
23609  *
23610  * Fork - LGPL
23611  * <script type="text/javascript">
23612  */
23613  (function(){ 
23614 /**
23615  * @class Roo.Layer
23616  * @extends Roo.Element
23617  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23618  * automatic maintaining of shadow/shim positions.
23619  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23620  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23621  * you can pass a string with a CSS class name. False turns off the shadow.
23622  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23623  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23624  * @cfg {String} cls CSS class to add to the element
23625  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23626  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23627  * @constructor
23628  * @param {Object} config An object with config options.
23629  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23630  */
23631
23632 Roo.Layer = function(config, existingEl){
23633     config = config || {};
23634     var dh = Roo.DomHelper;
23635     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23636     if(existingEl){
23637         this.dom = Roo.getDom(existingEl);
23638     }
23639     if(!this.dom){
23640         var o = config.dh || {tag: "div", cls: "x-layer"};
23641         this.dom = dh.append(pel, o);
23642     }
23643     if(config.cls){
23644         this.addClass(config.cls);
23645     }
23646     this.constrain = config.constrain !== false;
23647     this.visibilityMode = Roo.Element.VISIBILITY;
23648     if(config.id){
23649         this.id = this.dom.id = config.id;
23650     }else{
23651         this.id = Roo.id(this.dom);
23652     }
23653     this.zindex = config.zindex || this.getZIndex();
23654     this.position("absolute", this.zindex);
23655     if(config.shadow){
23656         this.shadowOffset = config.shadowOffset || 4;
23657         this.shadow = new Roo.Shadow({
23658             offset : this.shadowOffset,
23659             mode : config.shadow
23660         });
23661     }else{
23662         this.shadowOffset = 0;
23663     }
23664     this.useShim = config.shim !== false && Roo.useShims;
23665     this.useDisplay = config.useDisplay;
23666     this.hide();
23667 };
23668
23669 var supr = Roo.Element.prototype;
23670
23671 // shims are shared among layer to keep from having 100 iframes
23672 var shims = [];
23673
23674 Roo.extend(Roo.Layer, Roo.Element, {
23675
23676     getZIndex : function(){
23677         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23678     },
23679
23680     getShim : function(){
23681         if(!this.useShim){
23682             return null;
23683         }
23684         if(this.shim){
23685             return this.shim;
23686         }
23687         var shim = shims.shift();
23688         if(!shim){
23689             shim = this.createShim();
23690             shim.enableDisplayMode('block');
23691             shim.dom.style.display = 'none';
23692             shim.dom.style.visibility = 'visible';
23693         }
23694         var pn = this.dom.parentNode;
23695         if(shim.dom.parentNode != pn){
23696             pn.insertBefore(shim.dom, this.dom);
23697         }
23698         shim.setStyle('z-index', this.getZIndex()-2);
23699         this.shim = shim;
23700         return shim;
23701     },
23702
23703     hideShim : function(){
23704         if(this.shim){
23705             this.shim.setDisplayed(false);
23706             shims.push(this.shim);
23707             delete this.shim;
23708         }
23709     },
23710
23711     disableShadow : function(){
23712         if(this.shadow){
23713             this.shadowDisabled = true;
23714             this.shadow.hide();
23715             this.lastShadowOffset = this.shadowOffset;
23716             this.shadowOffset = 0;
23717         }
23718     },
23719
23720     enableShadow : function(show){
23721         if(this.shadow){
23722             this.shadowDisabled = false;
23723             this.shadowOffset = this.lastShadowOffset;
23724             delete this.lastShadowOffset;
23725             if(show){
23726                 this.sync(true);
23727             }
23728         }
23729     },
23730
23731     // private
23732     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23733     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23734     sync : function(doShow){
23735         var sw = this.shadow;
23736         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23737             var sh = this.getShim();
23738
23739             var w = this.getWidth(),
23740                 h = this.getHeight();
23741
23742             var l = this.getLeft(true),
23743                 t = this.getTop(true);
23744
23745             if(sw && !this.shadowDisabled){
23746                 if(doShow && !sw.isVisible()){
23747                     sw.show(this);
23748                 }else{
23749                     sw.realign(l, t, w, h);
23750                 }
23751                 if(sh){
23752                     if(doShow){
23753                        sh.show();
23754                     }
23755                     // fit the shim behind the shadow, so it is shimmed too
23756                     var a = sw.adjusts, s = sh.dom.style;
23757                     s.left = (Math.min(l, l+a.l))+"px";
23758                     s.top = (Math.min(t, t+a.t))+"px";
23759                     s.width = (w+a.w)+"px";
23760                     s.height = (h+a.h)+"px";
23761                 }
23762             }else if(sh){
23763                 if(doShow){
23764                    sh.show();
23765                 }
23766                 sh.setSize(w, h);
23767                 sh.setLeftTop(l, t);
23768             }
23769             
23770         }
23771     },
23772
23773     // private
23774     destroy : function(){
23775         this.hideShim();
23776         if(this.shadow){
23777             this.shadow.hide();
23778         }
23779         this.removeAllListeners();
23780         var pn = this.dom.parentNode;
23781         if(pn){
23782             pn.removeChild(this.dom);
23783         }
23784         Roo.Element.uncache(this.id);
23785     },
23786
23787     remove : function(){
23788         this.destroy();
23789     },
23790
23791     // private
23792     beginUpdate : function(){
23793         this.updating = true;
23794     },
23795
23796     // private
23797     endUpdate : function(){
23798         this.updating = false;
23799         this.sync(true);
23800     },
23801
23802     // private
23803     hideUnders : function(negOffset){
23804         if(this.shadow){
23805             this.shadow.hide();
23806         }
23807         this.hideShim();
23808     },
23809
23810     // private
23811     constrainXY : function(){
23812         if(this.constrain){
23813             var vw = Roo.lib.Dom.getViewWidth(),
23814                 vh = Roo.lib.Dom.getViewHeight();
23815             var s = Roo.get(document).getScroll();
23816
23817             var xy = this.getXY();
23818             var x = xy[0], y = xy[1];   
23819             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23820             // only move it if it needs it
23821             var moved = false;
23822             // first validate right/bottom
23823             if((x + w) > vw+s.left){
23824                 x = vw - w - this.shadowOffset;
23825                 moved = true;
23826             }
23827             if((y + h) > vh+s.top){
23828                 y = vh - h - this.shadowOffset;
23829                 moved = true;
23830             }
23831             // then make sure top/left isn't negative
23832             if(x < s.left){
23833                 x = s.left;
23834                 moved = true;
23835             }
23836             if(y < s.top){
23837                 y = s.top;
23838                 moved = true;
23839             }
23840             if(moved){
23841                 if(this.avoidY){
23842                     var ay = this.avoidY;
23843                     if(y <= ay && (y+h) >= ay){
23844                         y = ay-h-5;   
23845                     }
23846                 }
23847                 xy = [x, y];
23848                 this.storeXY(xy);
23849                 supr.setXY.call(this, xy);
23850                 this.sync();
23851             }
23852         }
23853     },
23854
23855     isVisible : function(){
23856         return this.visible;    
23857     },
23858
23859     // private
23860     showAction : function(){
23861         this.visible = true; // track visibility to prevent getStyle calls
23862         if(this.useDisplay === true){
23863             this.setDisplayed("");
23864         }else if(this.lastXY){
23865             supr.setXY.call(this, this.lastXY);
23866         }else if(this.lastLT){
23867             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23868         }
23869     },
23870
23871     // private
23872     hideAction : function(){
23873         this.visible = false;
23874         if(this.useDisplay === true){
23875             this.setDisplayed(false);
23876         }else{
23877             this.setLeftTop(-10000,-10000);
23878         }
23879     },
23880
23881     // overridden Element method
23882     setVisible : function(v, a, d, c, e){
23883         if(v){
23884             this.showAction();
23885         }
23886         if(a && v){
23887             var cb = function(){
23888                 this.sync(true);
23889                 if(c){
23890                     c();
23891                 }
23892             }.createDelegate(this);
23893             supr.setVisible.call(this, true, true, d, cb, e);
23894         }else{
23895             if(!v){
23896                 this.hideUnders(true);
23897             }
23898             var cb = c;
23899             if(a){
23900                 cb = function(){
23901                     this.hideAction();
23902                     if(c){
23903                         c();
23904                     }
23905                 }.createDelegate(this);
23906             }
23907             supr.setVisible.call(this, v, a, d, cb, e);
23908             if(v){
23909                 this.sync(true);
23910             }else if(!a){
23911                 this.hideAction();
23912             }
23913         }
23914     },
23915
23916     storeXY : function(xy){
23917         delete this.lastLT;
23918         this.lastXY = xy;
23919     },
23920
23921     storeLeftTop : function(left, top){
23922         delete this.lastXY;
23923         this.lastLT = [left, top];
23924     },
23925
23926     // private
23927     beforeFx : function(){
23928         this.beforeAction();
23929         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23930     },
23931
23932     // private
23933     afterFx : function(){
23934         Roo.Layer.superclass.afterFx.apply(this, arguments);
23935         this.sync(this.isVisible());
23936     },
23937
23938     // private
23939     beforeAction : function(){
23940         if(!this.updating && this.shadow){
23941             this.shadow.hide();
23942         }
23943     },
23944
23945     // overridden Element method
23946     setLeft : function(left){
23947         this.storeLeftTop(left, this.getTop(true));
23948         supr.setLeft.apply(this, arguments);
23949         this.sync();
23950     },
23951
23952     setTop : function(top){
23953         this.storeLeftTop(this.getLeft(true), top);
23954         supr.setTop.apply(this, arguments);
23955         this.sync();
23956     },
23957
23958     setLeftTop : function(left, top){
23959         this.storeLeftTop(left, top);
23960         supr.setLeftTop.apply(this, arguments);
23961         this.sync();
23962     },
23963
23964     setXY : function(xy, a, d, c, e){
23965         this.fixDisplay();
23966         this.beforeAction();
23967         this.storeXY(xy);
23968         var cb = this.createCB(c);
23969         supr.setXY.call(this, xy, a, d, cb, e);
23970         if(!a){
23971             cb();
23972         }
23973     },
23974
23975     // private
23976     createCB : function(c){
23977         var el = this;
23978         return function(){
23979             el.constrainXY();
23980             el.sync(true);
23981             if(c){
23982                 c();
23983             }
23984         };
23985     },
23986
23987     // overridden Element method
23988     setX : function(x, a, d, c, e){
23989         this.setXY([x, this.getY()], a, d, c, e);
23990     },
23991
23992     // overridden Element method
23993     setY : function(y, a, d, c, e){
23994         this.setXY([this.getX(), y], a, d, c, e);
23995     },
23996
23997     // overridden Element method
23998     setSize : function(w, h, a, d, c, e){
23999         this.beforeAction();
24000         var cb = this.createCB(c);
24001         supr.setSize.call(this, w, h, a, d, cb, e);
24002         if(!a){
24003             cb();
24004         }
24005     },
24006
24007     // overridden Element method
24008     setWidth : function(w, a, d, c, e){
24009         this.beforeAction();
24010         var cb = this.createCB(c);
24011         supr.setWidth.call(this, w, a, d, cb, e);
24012         if(!a){
24013             cb();
24014         }
24015     },
24016
24017     // overridden Element method
24018     setHeight : function(h, a, d, c, e){
24019         this.beforeAction();
24020         var cb = this.createCB(c);
24021         supr.setHeight.call(this, h, a, d, cb, e);
24022         if(!a){
24023             cb();
24024         }
24025     },
24026
24027     // overridden Element method
24028     setBounds : function(x, y, w, h, a, d, c, e){
24029         this.beforeAction();
24030         var cb = this.createCB(c);
24031         if(!a){
24032             this.storeXY([x, y]);
24033             supr.setXY.call(this, [x, y]);
24034             supr.setSize.call(this, w, h, a, d, cb, e);
24035             cb();
24036         }else{
24037             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24038         }
24039         return this;
24040     },
24041     
24042     /**
24043      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24044      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24045      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24046      * @param {Number} zindex The new z-index to set
24047      * @return {this} The Layer
24048      */
24049     setZIndex : function(zindex){
24050         this.zindex = zindex;
24051         this.setStyle("z-index", zindex + 2);
24052         if(this.shadow){
24053             this.shadow.setZIndex(zindex + 1);
24054         }
24055         if(this.shim){
24056             this.shim.setStyle("z-index", zindex);
24057         }
24058     }
24059 });
24060 })();/*
24061  * Based on:
24062  * Ext JS Library 1.1.1
24063  * Copyright(c) 2006-2007, Ext JS, LLC.
24064  *
24065  * Originally Released Under LGPL - original licence link has changed is not relivant.
24066  *
24067  * Fork - LGPL
24068  * <script type="text/javascript">
24069  */
24070
24071
24072 /**
24073  * @class Roo.Shadow
24074  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24075  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24076  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24077  * @constructor
24078  * Create a new Shadow
24079  * @param {Object} config The config object
24080  */
24081 Roo.Shadow = function(config){
24082     Roo.apply(this, config);
24083     if(typeof this.mode != "string"){
24084         this.mode = this.defaultMode;
24085     }
24086     var o = this.offset, a = {h: 0};
24087     var rad = Math.floor(this.offset/2);
24088     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24089         case "drop":
24090             a.w = 0;
24091             a.l = a.t = o;
24092             a.t -= 1;
24093             if(Roo.isIE){
24094                 a.l -= this.offset + rad;
24095                 a.t -= this.offset + rad;
24096                 a.w -= rad;
24097                 a.h -= rad;
24098                 a.t += 1;
24099             }
24100         break;
24101         case "sides":
24102             a.w = (o*2);
24103             a.l = -o;
24104             a.t = o-1;
24105             if(Roo.isIE){
24106                 a.l -= (this.offset - rad);
24107                 a.t -= this.offset + rad;
24108                 a.l += 1;
24109                 a.w -= (this.offset - rad)*2;
24110                 a.w -= rad + 1;
24111                 a.h -= 1;
24112             }
24113         break;
24114         case "frame":
24115             a.w = a.h = (o*2);
24116             a.l = a.t = -o;
24117             a.t += 1;
24118             a.h -= 2;
24119             if(Roo.isIE){
24120                 a.l -= (this.offset - rad);
24121                 a.t -= (this.offset - rad);
24122                 a.l += 1;
24123                 a.w -= (this.offset + rad + 1);
24124                 a.h -= (this.offset + rad);
24125                 a.h += 1;
24126             }
24127         break;
24128     };
24129
24130     this.adjusts = a;
24131 };
24132
24133 Roo.Shadow.prototype = {
24134     /**
24135      * @cfg {String} mode
24136      * The shadow display mode.  Supports the following options:<br />
24137      * sides: Shadow displays on both sides and bottom only<br />
24138      * frame: Shadow displays equally on all four sides<br />
24139      * drop: Traditional bottom-right drop shadow (default)
24140      */
24141     /**
24142      * @cfg {String} offset
24143      * The number of pixels to offset the shadow from the element (defaults to 4)
24144      */
24145     offset: 4,
24146
24147     // private
24148     defaultMode: "drop",
24149
24150     /**
24151      * Displays the shadow under the target element
24152      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24153      */
24154     show : function(target){
24155         target = Roo.get(target);
24156         if(!this.el){
24157             this.el = Roo.Shadow.Pool.pull();
24158             if(this.el.dom.nextSibling != target.dom){
24159                 this.el.insertBefore(target);
24160             }
24161         }
24162         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24163         if(Roo.isIE){
24164             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24165         }
24166         this.realign(
24167             target.getLeft(true),
24168             target.getTop(true),
24169             target.getWidth(),
24170             target.getHeight()
24171         );
24172         this.el.dom.style.display = "block";
24173     },
24174
24175     /**
24176      * Returns true if the shadow is visible, else false
24177      */
24178     isVisible : function(){
24179         return this.el ? true : false;  
24180     },
24181
24182     /**
24183      * Direct alignment when values are already available. Show must be called at least once before
24184      * calling this method to ensure it is initialized.
24185      * @param {Number} left The target element left position
24186      * @param {Number} top The target element top position
24187      * @param {Number} width The target element width
24188      * @param {Number} height The target element height
24189      */
24190     realign : function(l, t, w, h){
24191         if(!this.el){
24192             return;
24193         }
24194         var a = this.adjusts, d = this.el.dom, s = d.style;
24195         var iea = 0;
24196         s.left = (l+a.l)+"px";
24197         s.top = (t+a.t)+"px";
24198         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24199  
24200         if(s.width != sws || s.height != shs){
24201             s.width = sws;
24202             s.height = shs;
24203             if(!Roo.isIE){
24204                 var cn = d.childNodes;
24205                 var sww = Math.max(0, (sw-12))+"px";
24206                 cn[0].childNodes[1].style.width = sww;
24207                 cn[1].childNodes[1].style.width = sww;
24208                 cn[2].childNodes[1].style.width = sww;
24209                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24210             }
24211         }
24212     },
24213
24214     /**
24215      * Hides this shadow
24216      */
24217     hide : function(){
24218         if(this.el){
24219             this.el.dom.style.display = "none";
24220             Roo.Shadow.Pool.push(this.el);
24221             delete this.el;
24222         }
24223     },
24224
24225     /**
24226      * Adjust the z-index of this shadow
24227      * @param {Number} zindex The new z-index
24228      */
24229     setZIndex : function(z){
24230         this.zIndex = z;
24231         if(this.el){
24232             this.el.setStyle("z-index", z);
24233         }
24234     }
24235 };
24236
24237 // Private utility class that manages the internal Shadow cache
24238 Roo.Shadow.Pool = function(){
24239     var p = [];
24240     var markup = Roo.isIE ?
24241                  '<div class="x-ie-shadow"></div>' :
24242                  '<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>';
24243     return {
24244         pull : function(){
24245             var sh = p.shift();
24246             if(!sh){
24247                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24248                 sh.autoBoxAdjust = false;
24249             }
24250             return sh;
24251         },
24252
24253         push : function(sh){
24254             p.push(sh);
24255         }
24256     };
24257 }();/*
24258  * Based on:
24259  * Ext JS Library 1.1.1
24260  * Copyright(c) 2006-2007, Ext JS, LLC.
24261  *
24262  * Originally Released Under LGPL - original licence link has changed is not relivant.
24263  *
24264  * Fork - LGPL
24265  * <script type="text/javascript">
24266  */
24267
24268
24269 /**
24270  * @class Roo.SplitBar
24271  * @extends Roo.util.Observable
24272  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24273  * <br><br>
24274  * Usage:
24275  * <pre><code>
24276 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24277                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24278 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24279 split.minSize = 100;
24280 split.maxSize = 600;
24281 split.animate = true;
24282 split.on('moved', splitterMoved);
24283 </code></pre>
24284  * @constructor
24285  * Create a new SplitBar
24286  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24287  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24288  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24289  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24290                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24291                         position of the SplitBar).
24292  */
24293 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24294     
24295     /** @private */
24296     this.el = Roo.get(dragElement, true);
24297     this.el.dom.unselectable = "on";
24298     /** @private */
24299     this.resizingEl = Roo.get(resizingElement, true);
24300
24301     /**
24302      * @private
24303      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24304      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24305      * @type Number
24306      */
24307     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24308     
24309     /**
24310      * The minimum size of the resizing element. (Defaults to 0)
24311      * @type Number
24312      */
24313     this.minSize = 0;
24314     
24315     /**
24316      * The maximum size of the resizing element. (Defaults to 2000)
24317      * @type Number
24318      */
24319     this.maxSize = 2000;
24320     
24321     /**
24322      * Whether to animate the transition to the new size
24323      * @type Boolean
24324      */
24325     this.animate = false;
24326     
24327     /**
24328      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24329      * @type Boolean
24330      */
24331     this.useShim = false;
24332     
24333     /** @private */
24334     this.shim = null;
24335     
24336     if(!existingProxy){
24337         /** @private */
24338         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24339     }else{
24340         this.proxy = Roo.get(existingProxy).dom;
24341     }
24342     /** @private */
24343     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24344     
24345     /** @private */
24346     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24347     
24348     /** @private */
24349     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24350     
24351     /** @private */
24352     this.dragSpecs = {};
24353     
24354     /**
24355      * @private The adapter to use to positon and resize elements
24356      */
24357     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24358     this.adapter.init(this);
24359     
24360     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24361         /** @private */
24362         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24363         this.el.addClass("x-splitbar-h");
24364     }else{
24365         /** @private */
24366         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24367         this.el.addClass("x-splitbar-v");
24368     }
24369     
24370     this.addEvents({
24371         /**
24372          * @event resize
24373          * Fires when the splitter is moved (alias for {@link #event-moved})
24374          * @param {Roo.SplitBar} this
24375          * @param {Number} newSize the new width or height
24376          */
24377         "resize" : true,
24378         /**
24379          * @event moved
24380          * Fires when the splitter is moved
24381          * @param {Roo.SplitBar} this
24382          * @param {Number} newSize the new width or height
24383          */
24384         "moved" : true,
24385         /**
24386          * @event beforeresize
24387          * Fires before the splitter is dragged
24388          * @param {Roo.SplitBar} this
24389          */
24390         "beforeresize" : true,
24391
24392         "beforeapply" : true
24393     });
24394
24395     Roo.util.Observable.call(this);
24396 };
24397
24398 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24399     onStartProxyDrag : function(x, y){
24400         this.fireEvent("beforeresize", this);
24401         if(!this.overlay){
24402             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24403             o.unselectable();
24404             o.enableDisplayMode("block");
24405             // all splitbars share the same overlay
24406             Roo.SplitBar.prototype.overlay = o;
24407         }
24408         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24409         this.overlay.show();
24410         Roo.get(this.proxy).setDisplayed("block");
24411         var size = this.adapter.getElementSize(this);
24412         this.activeMinSize = this.getMinimumSize();;
24413         this.activeMaxSize = this.getMaximumSize();;
24414         var c1 = size - this.activeMinSize;
24415         var c2 = Math.max(this.activeMaxSize - size, 0);
24416         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24417             this.dd.resetConstraints();
24418             this.dd.setXConstraint(
24419                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24420                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24421             );
24422             this.dd.setYConstraint(0, 0);
24423         }else{
24424             this.dd.resetConstraints();
24425             this.dd.setXConstraint(0, 0);
24426             this.dd.setYConstraint(
24427                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24428                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24429             );
24430          }
24431         this.dragSpecs.startSize = size;
24432         this.dragSpecs.startPoint = [x, y];
24433         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24434     },
24435     
24436     /** 
24437      * @private Called after the drag operation by the DDProxy
24438      */
24439     onEndProxyDrag : function(e){
24440         Roo.get(this.proxy).setDisplayed(false);
24441         var endPoint = Roo.lib.Event.getXY(e);
24442         if(this.overlay){
24443             this.overlay.hide();
24444         }
24445         var newSize;
24446         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24447             newSize = this.dragSpecs.startSize + 
24448                 (this.placement == Roo.SplitBar.LEFT ?
24449                     endPoint[0] - this.dragSpecs.startPoint[0] :
24450                     this.dragSpecs.startPoint[0] - endPoint[0]
24451                 );
24452         }else{
24453             newSize = this.dragSpecs.startSize + 
24454                 (this.placement == Roo.SplitBar.TOP ?
24455                     endPoint[1] - this.dragSpecs.startPoint[1] :
24456                     this.dragSpecs.startPoint[1] - endPoint[1]
24457                 );
24458         }
24459         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24460         if(newSize != this.dragSpecs.startSize){
24461             if(this.fireEvent('beforeapply', this, newSize) !== false){
24462                 this.adapter.setElementSize(this, newSize);
24463                 this.fireEvent("moved", this, newSize);
24464                 this.fireEvent("resize", this, newSize);
24465             }
24466         }
24467     },
24468     
24469     /**
24470      * Get the adapter this SplitBar uses
24471      * @return The adapter object
24472      */
24473     getAdapter : function(){
24474         return this.adapter;
24475     },
24476     
24477     /**
24478      * Set the adapter this SplitBar uses
24479      * @param {Object} adapter A SplitBar adapter object
24480      */
24481     setAdapter : function(adapter){
24482         this.adapter = adapter;
24483         this.adapter.init(this);
24484     },
24485     
24486     /**
24487      * Gets the minimum size for the resizing element
24488      * @return {Number} The minimum size
24489      */
24490     getMinimumSize : function(){
24491         return this.minSize;
24492     },
24493     
24494     /**
24495      * Sets the minimum size for the resizing element
24496      * @param {Number} minSize The minimum size
24497      */
24498     setMinimumSize : function(minSize){
24499         this.minSize = minSize;
24500     },
24501     
24502     /**
24503      * Gets the maximum size for the resizing element
24504      * @return {Number} The maximum size
24505      */
24506     getMaximumSize : function(){
24507         return this.maxSize;
24508     },
24509     
24510     /**
24511      * Sets the maximum size for the resizing element
24512      * @param {Number} maxSize The maximum size
24513      */
24514     setMaximumSize : function(maxSize){
24515         this.maxSize = maxSize;
24516     },
24517     
24518     /**
24519      * Sets the initialize size for the resizing element
24520      * @param {Number} size The initial size
24521      */
24522     setCurrentSize : function(size){
24523         var oldAnimate = this.animate;
24524         this.animate = false;
24525         this.adapter.setElementSize(this, size);
24526         this.animate = oldAnimate;
24527     },
24528     
24529     /**
24530      * Destroy this splitbar. 
24531      * @param {Boolean} removeEl True to remove the element
24532      */
24533     destroy : function(removeEl){
24534         if(this.shim){
24535             this.shim.remove();
24536         }
24537         this.dd.unreg();
24538         this.proxy.parentNode.removeChild(this.proxy);
24539         if(removeEl){
24540             this.el.remove();
24541         }
24542     }
24543 });
24544
24545 /**
24546  * @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.
24547  */
24548 Roo.SplitBar.createProxy = function(dir){
24549     var proxy = new Roo.Element(document.createElement("div"));
24550     proxy.unselectable();
24551     var cls = 'x-splitbar-proxy';
24552     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24553     document.body.appendChild(proxy.dom);
24554     return proxy.dom;
24555 };
24556
24557 /** 
24558  * @class Roo.SplitBar.BasicLayoutAdapter
24559  * Default Adapter. It assumes the splitter and resizing element are not positioned
24560  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24561  */
24562 Roo.SplitBar.BasicLayoutAdapter = function(){
24563 };
24564
24565 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24566     // do nothing for now
24567     init : function(s){
24568     
24569     },
24570     /**
24571      * Called before drag operations to get the current size of the resizing element. 
24572      * @param {Roo.SplitBar} s The SplitBar using this adapter
24573      */
24574      getElementSize : function(s){
24575         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24576             return s.resizingEl.getWidth();
24577         }else{
24578             return s.resizingEl.getHeight();
24579         }
24580     },
24581     
24582     /**
24583      * Called after drag operations to set the size of the resizing element.
24584      * @param {Roo.SplitBar} s The SplitBar using this adapter
24585      * @param {Number} newSize The new size to set
24586      * @param {Function} onComplete A function to be invoked when resizing is complete
24587      */
24588     setElementSize : function(s, newSize, onComplete){
24589         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24590             if(!s.animate){
24591                 s.resizingEl.setWidth(newSize);
24592                 if(onComplete){
24593                     onComplete(s, newSize);
24594                 }
24595             }else{
24596                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24597             }
24598         }else{
24599             
24600             if(!s.animate){
24601                 s.resizingEl.setHeight(newSize);
24602                 if(onComplete){
24603                     onComplete(s, newSize);
24604                 }
24605             }else{
24606                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24607             }
24608         }
24609     }
24610 };
24611
24612 /** 
24613  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24614  * @extends Roo.SplitBar.BasicLayoutAdapter
24615  * Adapter that  moves the splitter element to align with the resized sizing element. 
24616  * Used with an absolute positioned SplitBar.
24617  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24618  * document.body, make sure you assign an id to the body element.
24619  */
24620 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24621     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24622     this.container = Roo.get(container);
24623 };
24624
24625 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24626     init : function(s){
24627         this.basic.init(s);
24628     },
24629     
24630     getElementSize : function(s){
24631         return this.basic.getElementSize(s);
24632     },
24633     
24634     setElementSize : function(s, newSize, onComplete){
24635         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24636     },
24637     
24638     moveSplitter : function(s){
24639         var yes = Roo.SplitBar;
24640         switch(s.placement){
24641             case yes.LEFT:
24642                 s.el.setX(s.resizingEl.getRight());
24643                 break;
24644             case yes.RIGHT:
24645                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24646                 break;
24647             case yes.TOP:
24648                 s.el.setY(s.resizingEl.getBottom());
24649                 break;
24650             case yes.BOTTOM:
24651                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24652                 break;
24653         }
24654     }
24655 };
24656
24657 /**
24658  * Orientation constant - Create a vertical SplitBar
24659  * @static
24660  * @type Number
24661  */
24662 Roo.SplitBar.VERTICAL = 1;
24663
24664 /**
24665  * Orientation constant - Create a horizontal SplitBar
24666  * @static
24667  * @type Number
24668  */
24669 Roo.SplitBar.HORIZONTAL = 2;
24670
24671 /**
24672  * Placement constant - The resizing element is to the left of the splitter element
24673  * @static
24674  * @type Number
24675  */
24676 Roo.SplitBar.LEFT = 1;
24677
24678 /**
24679  * Placement constant - The resizing element is to the right of the splitter element
24680  * @static
24681  * @type Number
24682  */
24683 Roo.SplitBar.RIGHT = 2;
24684
24685 /**
24686  * Placement constant - The resizing element is positioned above the splitter element
24687  * @static
24688  * @type Number
24689  */
24690 Roo.SplitBar.TOP = 3;
24691
24692 /**
24693  * Placement constant - The resizing element is positioned under splitter element
24694  * @static
24695  * @type Number
24696  */
24697 Roo.SplitBar.BOTTOM = 4;
24698 /*
24699  * Based on:
24700  * Ext JS Library 1.1.1
24701  * Copyright(c) 2006-2007, Ext JS, LLC.
24702  *
24703  * Originally Released Under LGPL - original licence link has changed is not relivant.
24704  *
24705  * Fork - LGPL
24706  * <script type="text/javascript">
24707  */
24708
24709 /**
24710  * @class Roo.View
24711  * @extends Roo.util.Observable
24712  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24713  * This class also supports single and multi selection modes. <br>
24714  * Create a data model bound view:
24715  <pre><code>
24716  var store = new Roo.data.Store(...);
24717
24718  var view = new Roo.View({
24719     el : "my-element",
24720     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24721  
24722     singleSelect: true,
24723     selectedClass: "ydataview-selected",
24724     store: store
24725  });
24726
24727  // listen for node click?
24728  view.on("click", function(vw, index, node, e){
24729  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24730  });
24731
24732  // load XML data
24733  dataModel.load("foobar.xml");
24734  </code></pre>
24735  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24736  * <br><br>
24737  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24738  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24739  * 
24740  * Note: old style constructor is still suported (container, template, config)
24741  * 
24742  * @constructor
24743  * Create a new View
24744  * @param {Object} config The config object
24745  * 
24746  */
24747 Roo.View = function(config, depreciated_tpl, depreciated_config){
24748     
24749     if (typeof(depreciated_tpl) == 'undefined') {
24750         // new way.. - universal constructor.
24751         Roo.apply(this, config);
24752         this.el  = Roo.get(this.el);
24753     } else {
24754         // old format..
24755         this.el  = Roo.get(config);
24756         this.tpl = depreciated_tpl;
24757         Roo.apply(this, depreciated_config);
24758     }
24759     this.wrapEl  = this.el.wrap().wrap();
24760     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24761     
24762     
24763     if(typeof(this.tpl) == "string"){
24764         this.tpl = new Roo.Template(this.tpl);
24765     } else {
24766         // support xtype ctors..
24767         this.tpl = new Roo.factory(this.tpl, Roo);
24768     }
24769     
24770     
24771     this.tpl.compile();
24772    
24773   
24774     
24775      
24776     /** @private */
24777     this.addEvents({
24778         /**
24779          * @event beforeclick
24780          * Fires before a click is processed. Returns false to cancel the default action.
24781          * @param {Roo.View} this
24782          * @param {Number} index The index of the target node
24783          * @param {HTMLElement} node The target node
24784          * @param {Roo.EventObject} e The raw event object
24785          */
24786             "beforeclick" : true,
24787         /**
24788          * @event click
24789          * Fires when a template node is clicked.
24790          * @param {Roo.View} this
24791          * @param {Number} index The index of the target node
24792          * @param {HTMLElement} node The target node
24793          * @param {Roo.EventObject} e The raw event object
24794          */
24795             "click" : true,
24796         /**
24797          * @event dblclick
24798          * Fires when a template node is double clicked.
24799          * @param {Roo.View} this
24800          * @param {Number} index The index of the target node
24801          * @param {HTMLElement} node The target node
24802          * @param {Roo.EventObject} e The raw event object
24803          */
24804             "dblclick" : true,
24805         /**
24806          * @event contextmenu
24807          * Fires when a template node is right clicked.
24808          * @param {Roo.View} this
24809          * @param {Number} index The index of the target node
24810          * @param {HTMLElement} node The target node
24811          * @param {Roo.EventObject} e The raw event object
24812          */
24813             "contextmenu" : true,
24814         /**
24815          * @event selectionchange
24816          * Fires when the selected nodes change.
24817          * @param {Roo.View} this
24818          * @param {Array} selections Array of the selected nodes
24819          */
24820             "selectionchange" : true,
24821     
24822         /**
24823          * @event beforeselect
24824          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24825          * @param {Roo.View} this
24826          * @param {HTMLElement} node The node to be selected
24827          * @param {Array} selections Array of currently selected nodes
24828          */
24829             "beforeselect" : true,
24830         /**
24831          * @event preparedata
24832          * Fires on every row to render, to allow you to change the data.
24833          * @param {Roo.View} this
24834          * @param {Object} data to be rendered (change this)
24835          */
24836           "preparedata" : true
24837           
24838           
24839         });
24840
24841
24842
24843     this.el.on({
24844         "click": this.onClick,
24845         "dblclick": this.onDblClick,
24846         "contextmenu": this.onContextMenu,
24847         scope:this
24848     });
24849
24850     this.selections = [];
24851     this.nodes = [];
24852     this.cmp = new Roo.CompositeElementLite([]);
24853     if(this.store){
24854         this.store = Roo.factory(this.store, Roo.data);
24855         this.setStore(this.store, true);
24856     }
24857     
24858     if ( this.footer && this.footer.xtype) {
24859            
24860          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24861         
24862         this.footer.dataSource = this.store
24863         this.footer.container = fctr;
24864         this.footer = Roo.factory(this.footer, Roo);
24865         fctr.insertFirst(this.el);
24866         
24867         // this is a bit insane - as the paging toolbar seems to detach the el..
24868 //        dom.parentNode.parentNode.parentNode
24869          // they get detached?
24870     }
24871     
24872     
24873     Roo.View.superclass.constructor.call(this);
24874     
24875     
24876 };
24877
24878 Roo.extend(Roo.View, Roo.util.Observable, {
24879     
24880      /**
24881      * @cfg {Roo.data.Store} store Data store to load data from.
24882      */
24883     store : false,
24884     
24885     /**
24886      * @cfg {String|Roo.Element} el The container element.
24887      */
24888     el : '',
24889     
24890     /**
24891      * @cfg {String|Roo.Template} tpl The template used by this View 
24892      */
24893     tpl : false,
24894     /**
24895      * @cfg {String} dataName the named area of the template to use as the data area
24896      *                          Works with domtemplates roo-name="name"
24897      */
24898     dataName: false,
24899     /**
24900      * @cfg {String} selectedClass The css class to add to selected nodes
24901      */
24902     selectedClass : "x-view-selected",
24903      /**
24904      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24905      */
24906     emptyText : "",
24907     
24908     /**
24909      * @cfg {String} text to display on mask (default Loading)
24910      */
24911     mask : false,
24912     /**
24913      * @cfg {Boolean} multiSelect Allow multiple selection
24914      */
24915     multiSelect : false,
24916     /**
24917      * @cfg {Boolean} singleSelect Allow single selection
24918      */
24919     singleSelect:  false,
24920     
24921     /**
24922      * @cfg {Boolean} toggleSelect - selecting 
24923      */
24924     toggleSelect : false,
24925     
24926     /**
24927      * Returns the element this view is bound to.
24928      * @return {Roo.Element}
24929      */
24930     getEl : function(){
24931         return this.wrapEl;
24932     },
24933     
24934     
24935
24936     /**
24937      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24938      */
24939     refresh : function(){
24940         Roo.log('refresh');
24941         var t = this.tpl;
24942         
24943         // if we are using something like 'domtemplate', then
24944         // the what gets used is:
24945         // t.applySubtemplate(NAME, data, wrapping data..)
24946         // the outer template then get' applied with
24947         //     the store 'extra data'
24948         // and the body get's added to the
24949         //      roo-name="data" node?
24950         //      <span class='roo-tpl-{name}'></span> ?????
24951         
24952         
24953         
24954         this.clearSelections();
24955         this.el.update("");
24956         var html = [];
24957         var records = this.store.getRange();
24958         if(records.length < 1) {
24959             
24960             // is this valid??  = should it render a template??
24961             
24962             this.el.update(this.emptyText);
24963             return;
24964         }
24965         var el = this.el;
24966         if (this.dataName) {
24967             this.el.update(t.apply(this.store.meta)); //????
24968             el = this.el.child('.roo-tpl-' + this.dataName);
24969         }
24970         
24971         for(var i = 0, len = records.length; i < len; i++){
24972             var data = this.prepareData(records[i].data, i, records[i]);
24973             this.fireEvent("preparedata", this, data, i, records[i]);
24974             html[html.length] = Roo.util.Format.trim(
24975                 this.dataName ?
24976                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24977                     t.apply(data)
24978             );
24979         }
24980         
24981         
24982         
24983         el.update(html.join(""));
24984         this.nodes = el.dom.childNodes;
24985         this.updateIndexes(0);
24986     },
24987     
24988
24989     /**
24990      * Function to override to reformat the data that is sent to
24991      * the template for each node.
24992      * DEPRICATED - use the preparedata event handler.
24993      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24994      * a JSON object for an UpdateManager bound view).
24995      */
24996     prepareData : function(data, index, record)
24997     {
24998         this.fireEvent("preparedata", this, data, index, record);
24999         return data;
25000     },
25001
25002     onUpdate : function(ds, record){
25003          Roo.log('on update');   
25004         this.clearSelections();
25005         var index = this.store.indexOf(record);
25006         var n = this.nodes[index];
25007         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25008         n.parentNode.removeChild(n);
25009         this.updateIndexes(index, index);
25010     },
25011
25012     
25013     
25014 // --------- FIXME     
25015     onAdd : function(ds, records, index)
25016     {
25017         Roo.log(['on Add', ds, records, index] );        
25018         this.clearSelections();
25019         if(this.nodes.length == 0){
25020             this.refresh();
25021             return;
25022         }
25023         var n = this.nodes[index];
25024         for(var i = 0, len = records.length; i < len; i++){
25025             var d = this.prepareData(records[i].data, i, records[i]);
25026             if(n){
25027                 this.tpl.insertBefore(n, d);
25028             }else{
25029                 
25030                 this.tpl.append(this.el, d);
25031             }
25032         }
25033         this.updateIndexes(index);
25034     },
25035
25036     onRemove : function(ds, record, index){
25037         Roo.log('onRemove');
25038         this.clearSelections();
25039         var el = this.dataName  ?
25040             this.el.child('.roo-tpl-' + this.dataName) :
25041             this.el; 
25042         
25043         el.dom.removeChild(this.nodes[index]);
25044         this.updateIndexes(index);
25045     },
25046
25047     /**
25048      * Refresh an individual node.
25049      * @param {Number} index
25050      */
25051     refreshNode : function(index){
25052         this.onUpdate(this.store, this.store.getAt(index));
25053     },
25054
25055     updateIndexes : function(startIndex, endIndex){
25056         var ns = this.nodes;
25057         startIndex = startIndex || 0;
25058         endIndex = endIndex || ns.length - 1;
25059         for(var i = startIndex; i <= endIndex; i++){
25060             ns[i].nodeIndex = i;
25061         }
25062     },
25063
25064     /**
25065      * Changes the data store this view uses and refresh the view.
25066      * @param {Store} store
25067      */
25068     setStore : function(store, initial){
25069         if(!initial && this.store){
25070             this.store.un("datachanged", this.refresh);
25071             this.store.un("add", this.onAdd);
25072             this.store.un("remove", this.onRemove);
25073             this.store.un("update", this.onUpdate);
25074             this.store.un("clear", this.refresh);
25075             this.store.un("beforeload", this.onBeforeLoad);
25076             this.store.un("load", this.onLoad);
25077             this.store.un("loadexception", this.onLoad);
25078         }
25079         if(store){
25080           
25081             store.on("datachanged", this.refresh, this);
25082             store.on("add", this.onAdd, this);
25083             store.on("remove", this.onRemove, this);
25084             store.on("update", this.onUpdate, this);
25085             store.on("clear", this.refresh, this);
25086             store.on("beforeload", this.onBeforeLoad, this);
25087             store.on("load", this.onLoad, this);
25088             store.on("loadexception", this.onLoad, this);
25089         }
25090         
25091         if(store){
25092             this.refresh();
25093         }
25094     },
25095     /**
25096      * onbeforeLoad - masks the loading area.
25097      *
25098      */
25099     onBeforeLoad : function(store,opts)
25100     {
25101          Roo.log('onBeforeLoad');   
25102         if (!opts.add) {
25103             this.el.update("");
25104         }
25105         this.el.mask(this.mask ? this.mask : "Loading" ); 
25106     },
25107     onLoad : function ()
25108     {
25109         this.el.unmask();
25110     },
25111     
25112
25113     /**
25114      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25115      * @param {HTMLElement} node
25116      * @return {HTMLElement} The template node
25117      */
25118     findItemFromChild : function(node){
25119         var el = this.dataName  ?
25120             this.el.child('.roo-tpl-' + this.dataName,true) :
25121             this.el.dom; 
25122         
25123         if(!node || node.parentNode == el){
25124                     return node;
25125             }
25126             var p = node.parentNode;
25127             while(p && p != el){
25128             if(p.parentNode == el){
25129                 return p;
25130             }
25131             p = p.parentNode;
25132         }
25133             return null;
25134     },
25135
25136     /** @ignore */
25137     onClick : function(e){
25138         var item = this.findItemFromChild(e.getTarget());
25139         if(item){
25140             var index = this.indexOf(item);
25141             if(this.onItemClick(item, index, e) !== false){
25142                 this.fireEvent("click", this, index, item, e);
25143             }
25144         }else{
25145             this.clearSelections();
25146         }
25147     },
25148
25149     /** @ignore */
25150     onContextMenu : function(e){
25151         var item = this.findItemFromChild(e.getTarget());
25152         if(item){
25153             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25154         }
25155     },
25156
25157     /** @ignore */
25158     onDblClick : function(e){
25159         var item = this.findItemFromChild(e.getTarget());
25160         if(item){
25161             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25162         }
25163     },
25164
25165     onItemClick : function(item, index, e)
25166     {
25167         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25168             return false;
25169         }
25170         if (this.toggleSelect) {
25171             var m = this.isSelected(item) ? 'unselect' : 'select';
25172             Roo.log(m);
25173             var _t = this;
25174             _t[m](item, true, false);
25175             return true;
25176         }
25177         if(this.multiSelect || this.singleSelect){
25178             if(this.multiSelect && e.shiftKey && this.lastSelection){
25179                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25180             }else{
25181                 this.select(item, this.multiSelect && e.ctrlKey);
25182                 this.lastSelection = item;
25183             }
25184             e.preventDefault();
25185         }
25186         return true;
25187     },
25188
25189     /**
25190      * Get the number of selected nodes.
25191      * @return {Number}
25192      */
25193     getSelectionCount : function(){
25194         return this.selections.length;
25195     },
25196
25197     /**
25198      * Get the currently selected nodes.
25199      * @return {Array} An array of HTMLElements
25200      */
25201     getSelectedNodes : function(){
25202         return this.selections;
25203     },
25204
25205     /**
25206      * Get the indexes of the selected nodes.
25207      * @return {Array}
25208      */
25209     getSelectedIndexes : function(){
25210         var indexes = [], s = this.selections;
25211         for(var i = 0, len = s.length; i < len; i++){
25212             indexes.push(s[i].nodeIndex);
25213         }
25214         return indexes;
25215     },
25216
25217     /**
25218      * Clear all selections
25219      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25220      */
25221     clearSelections : function(suppressEvent){
25222         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25223             this.cmp.elements = this.selections;
25224             this.cmp.removeClass(this.selectedClass);
25225             this.selections = [];
25226             if(!suppressEvent){
25227                 this.fireEvent("selectionchange", this, this.selections);
25228             }
25229         }
25230     },
25231
25232     /**
25233      * Returns true if the passed node is selected
25234      * @param {HTMLElement/Number} node The node or node index
25235      * @return {Boolean}
25236      */
25237     isSelected : function(node){
25238         var s = this.selections;
25239         if(s.length < 1){
25240             return false;
25241         }
25242         node = this.getNode(node);
25243         return s.indexOf(node) !== -1;
25244     },
25245
25246     /**
25247      * Selects nodes.
25248      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25249      * @param {Boolean} keepExisting (optional) true to keep existing selections
25250      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25251      */
25252     select : function(nodeInfo, keepExisting, suppressEvent){
25253         if(nodeInfo instanceof Array){
25254             if(!keepExisting){
25255                 this.clearSelections(true);
25256             }
25257             for(var i = 0, len = nodeInfo.length; i < len; i++){
25258                 this.select(nodeInfo[i], true, true);
25259             }
25260             return;
25261         } 
25262         var node = this.getNode(nodeInfo);
25263         if(!node || this.isSelected(node)){
25264             return; // already selected.
25265         }
25266         if(!keepExisting){
25267             this.clearSelections(true);
25268         }
25269         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25270             Roo.fly(node).addClass(this.selectedClass);
25271             this.selections.push(node);
25272             if(!suppressEvent){
25273                 this.fireEvent("selectionchange", this, this.selections);
25274             }
25275         }
25276         
25277         
25278     },
25279       /**
25280      * Unselects nodes.
25281      * @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
25282      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25283      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25284      */
25285     unselect : function(nodeInfo, keepExisting, suppressEvent)
25286     {
25287         if(nodeInfo instanceof Array){
25288             Roo.each(this.selections, function(s) {
25289                 this.unselect(s, nodeInfo);
25290             }, this);
25291             return;
25292         }
25293         var node = this.getNode(nodeInfo);
25294         if(!node || !this.isSelected(node)){
25295             Roo.log("not selected");
25296             return; // not selected.
25297         }
25298         // fireevent???
25299         var ns = [];
25300         Roo.each(this.selections, function(s) {
25301             if (s == node ) {
25302                 Roo.fly(node).removeClass(this.selectedClass);
25303
25304                 return;
25305             }
25306             ns.push(s);
25307         },this);
25308         
25309         this.selections= ns;
25310         this.fireEvent("selectionchange", this, this.selections);
25311     },
25312
25313     /**
25314      * Gets a template node.
25315      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25316      * @return {HTMLElement} The node or null if it wasn't found
25317      */
25318     getNode : function(nodeInfo){
25319         if(typeof nodeInfo == "string"){
25320             return document.getElementById(nodeInfo);
25321         }else if(typeof nodeInfo == "number"){
25322             return this.nodes[nodeInfo];
25323         }
25324         return nodeInfo;
25325     },
25326
25327     /**
25328      * Gets a range template nodes.
25329      * @param {Number} startIndex
25330      * @param {Number} endIndex
25331      * @return {Array} An array of nodes
25332      */
25333     getNodes : function(start, end){
25334         var ns = this.nodes;
25335         start = start || 0;
25336         end = typeof end == "undefined" ? ns.length - 1 : end;
25337         var nodes = [];
25338         if(start <= end){
25339             for(var i = start; i <= end; i++){
25340                 nodes.push(ns[i]);
25341             }
25342         } else{
25343             for(var i = start; i >= end; i--){
25344                 nodes.push(ns[i]);
25345             }
25346         }
25347         return nodes;
25348     },
25349
25350     /**
25351      * Finds the index of the passed node
25352      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25353      * @return {Number} The index of the node or -1
25354      */
25355     indexOf : function(node){
25356         node = this.getNode(node);
25357         if(typeof node.nodeIndex == "number"){
25358             return node.nodeIndex;
25359         }
25360         var ns = this.nodes;
25361         for(var i = 0, len = ns.length; i < len; i++){
25362             if(ns[i] == node){
25363                 return i;
25364             }
25365         }
25366         return -1;
25367     }
25368 });
25369 /*
25370  * Based on:
25371  * Ext JS Library 1.1.1
25372  * Copyright(c) 2006-2007, Ext JS, LLC.
25373  *
25374  * Originally Released Under LGPL - original licence link has changed is not relivant.
25375  *
25376  * Fork - LGPL
25377  * <script type="text/javascript">
25378  */
25379
25380 /**
25381  * @class Roo.JsonView
25382  * @extends Roo.View
25383  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25384 <pre><code>
25385 var view = new Roo.JsonView({
25386     container: "my-element",
25387     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25388     multiSelect: true, 
25389     jsonRoot: "data" 
25390 });
25391
25392 // listen for node click?
25393 view.on("click", function(vw, index, node, e){
25394     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25395 });
25396
25397 // direct load of JSON data
25398 view.load("foobar.php");
25399
25400 // Example from my blog list
25401 var tpl = new Roo.Template(
25402     '&lt;div class="entry"&gt;' +
25403     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25404     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25405     "&lt;/div&gt;&lt;hr /&gt;"
25406 );
25407
25408 var moreView = new Roo.JsonView({
25409     container :  "entry-list", 
25410     template : tpl,
25411     jsonRoot: "posts"
25412 });
25413 moreView.on("beforerender", this.sortEntries, this);
25414 moreView.load({
25415     url: "/blog/get-posts.php",
25416     params: "allposts=true",
25417     text: "Loading Blog Entries..."
25418 });
25419 </code></pre>
25420
25421 * Note: old code is supported with arguments : (container, template, config)
25422
25423
25424  * @constructor
25425  * Create a new JsonView
25426  * 
25427  * @param {Object} config The config object
25428  * 
25429  */
25430 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25431     
25432     
25433     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25434
25435     var um = this.el.getUpdateManager();
25436     um.setRenderer(this);
25437     um.on("update", this.onLoad, this);
25438     um.on("failure", this.onLoadException, this);
25439
25440     /**
25441      * @event beforerender
25442      * Fires before rendering of the downloaded JSON data.
25443      * @param {Roo.JsonView} this
25444      * @param {Object} data The JSON data loaded
25445      */
25446     /**
25447      * @event load
25448      * Fires when data is loaded.
25449      * @param {Roo.JsonView} this
25450      * @param {Object} data The JSON data loaded
25451      * @param {Object} response The raw Connect response object
25452      */
25453     /**
25454      * @event loadexception
25455      * Fires when loading fails.
25456      * @param {Roo.JsonView} this
25457      * @param {Object} response The raw Connect response object
25458      */
25459     this.addEvents({
25460         'beforerender' : true,
25461         'load' : true,
25462         'loadexception' : true
25463     });
25464 };
25465 Roo.extend(Roo.JsonView, Roo.View, {
25466     /**
25467      * @type {String} The root property in the loaded JSON object that contains the data
25468      */
25469     jsonRoot : "",
25470
25471     /**
25472      * Refreshes the view.
25473      */
25474     refresh : function(){
25475         this.clearSelections();
25476         this.el.update("");
25477         var html = [];
25478         var o = this.jsonData;
25479         if(o && o.length > 0){
25480             for(var i = 0, len = o.length; i < len; i++){
25481                 var data = this.prepareData(o[i], i, o);
25482                 html[html.length] = this.tpl.apply(data);
25483             }
25484         }else{
25485             html.push(this.emptyText);
25486         }
25487         this.el.update(html.join(""));
25488         this.nodes = this.el.dom.childNodes;
25489         this.updateIndexes(0);
25490     },
25491
25492     /**
25493      * 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.
25494      * @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:
25495      <pre><code>
25496      view.load({
25497          url: "your-url.php",
25498          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25499          callback: yourFunction,
25500          scope: yourObject, //(optional scope)
25501          discardUrl: false,
25502          nocache: false,
25503          text: "Loading...",
25504          timeout: 30,
25505          scripts: false
25506      });
25507      </code></pre>
25508      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25509      * 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.
25510      * @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}
25511      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25512      * @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.
25513      */
25514     load : function(){
25515         var um = this.el.getUpdateManager();
25516         um.update.apply(um, arguments);
25517     },
25518
25519     render : function(el, response){
25520         this.clearSelections();
25521         this.el.update("");
25522         var o;
25523         try{
25524             o = Roo.util.JSON.decode(response.responseText);
25525             if(this.jsonRoot){
25526                 
25527                 o = o[this.jsonRoot];
25528             }
25529         } catch(e){
25530         }
25531         /**
25532          * The current JSON data or null
25533          */
25534         this.jsonData = o;
25535         this.beforeRender();
25536         this.refresh();
25537     },
25538
25539 /**
25540  * Get the number of records in the current JSON dataset
25541  * @return {Number}
25542  */
25543     getCount : function(){
25544         return this.jsonData ? this.jsonData.length : 0;
25545     },
25546
25547 /**
25548  * Returns the JSON object for the specified node(s)
25549  * @param {HTMLElement/Array} node The node or an array of nodes
25550  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25551  * you get the JSON object for the node
25552  */
25553     getNodeData : function(node){
25554         if(node instanceof Array){
25555             var data = [];
25556             for(var i = 0, len = node.length; i < len; i++){
25557                 data.push(this.getNodeData(node[i]));
25558             }
25559             return data;
25560         }
25561         return this.jsonData[this.indexOf(node)] || null;
25562     },
25563
25564     beforeRender : function(){
25565         this.snapshot = this.jsonData;
25566         if(this.sortInfo){
25567             this.sort.apply(this, this.sortInfo);
25568         }
25569         this.fireEvent("beforerender", this, this.jsonData);
25570     },
25571
25572     onLoad : function(el, o){
25573         this.fireEvent("load", this, this.jsonData, o);
25574     },
25575
25576     onLoadException : function(el, o){
25577         this.fireEvent("loadexception", this, o);
25578     },
25579
25580 /**
25581  * Filter the data by a specific property.
25582  * @param {String} property A property on your JSON objects
25583  * @param {String/RegExp} value Either string that the property values
25584  * should start with, or a RegExp to test against the property
25585  */
25586     filter : function(property, value){
25587         if(this.jsonData){
25588             var data = [];
25589             var ss = this.snapshot;
25590             if(typeof value == "string"){
25591                 var vlen = value.length;
25592                 if(vlen == 0){
25593                     this.clearFilter();
25594                     return;
25595                 }
25596                 value = value.toLowerCase();
25597                 for(var i = 0, len = ss.length; i < len; i++){
25598                     var o = ss[i];
25599                     if(o[property].substr(0, vlen).toLowerCase() == value){
25600                         data.push(o);
25601                     }
25602                 }
25603             } else if(value.exec){ // regex?
25604                 for(var i = 0, len = ss.length; i < len; i++){
25605                     var o = ss[i];
25606                     if(value.test(o[property])){
25607                         data.push(o);
25608                     }
25609                 }
25610             } else{
25611                 return;
25612             }
25613             this.jsonData = data;
25614             this.refresh();
25615         }
25616     },
25617
25618 /**
25619  * Filter by a function. The passed function will be called with each
25620  * object in the current dataset. If the function returns true the value is kept,
25621  * otherwise it is filtered.
25622  * @param {Function} fn
25623  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25624  */
25625     filterBy : function(fn, scope){
25626         if(this.jsonData){
25627             var data = [];
25628             var ss = this.snapshot;
25629             for(var i = 0, len = ss.length; i < len; i++){
25630                 var o = ss[i];
25631                 if(fn.call(scope || this, o)){
25632                     data.push(o);
25633                 }
25634             }
25635             this.jsonData = data;
25636             this.refresh();
25637         }
25638     },
25639
25640 /**
25641  * Clears the current filter.
25642  */
25643     clearFilter : function(){
25644         if(this.snapshot && this.jsonData != this.snapshot){
25645             this.jsonData = this.snapshot;
25646             this.refresh();
25647         }
25648     },
25649
25650
25651 /**
25652  * Sorts the data for this view and refreshes it.
25653  * @param {String} property A property on your JSON objects to sort on
25654  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25655  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25656  */
25657     sort : function(property, dir, sortType){
25658         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25659         if(this.jsonData){
25660             var p = property;
25661             var dsc = dir && dir.toLowerCase() == "desc";
25662             var f = function(o1, o2){
25663                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25664                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25665                 ;
25666                 if(v1 < v2){
25667                     return dsc ? +1 : -1;
25668                 } else if(v1 > v2){
25669                     return dsc ? -1 : +1;
25670                 } else{
25671                     return 0;
25672                 }
25673             };
25674             this.jsonData.sort(f);
25675             this.refresh();
25676             if(this.jsonData != this.snapshot){
25677                 this.snapshot.sort(f);
25678             }
25679         }
25680     }
25681 });/*
25682  * Based on:
25683  * Ext JS Library 1.1.1
25684  * Copyright(c) 2006-2007, Ext JS, LLC.
25685  *
25686  * Originally Released Under LGPL - original licence link has changed is not relivant.
25687  *
25688  * Fork - LGPL
25689  * <script type="text/javascript">
25690  */
25691  
25692
25693 /**
25694  * @class Roo.ColorPalette
25695  * @extends Roo.Component
25696  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25697  * Here's an example of typical usage:
25698  * <pre><code>
25699 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25700 cp.render('my-div');
25701
25702 cp.on('select', function(palette, selColor){
25703     // do something with selColor
25704 });
25705 </code></pre>
25706  * @constructor
25707  * Create a new ColorPalette
25708  * @param {Object} config The config object
25709  */
25710 Roo.ColorPalette = function(config){
25711     Roo.ColorPalette.superclass.constructor.call(this, config);
25712     this.addEvents({
25713         /**
25714              * @event select
25715              * Fires when a color is selected
25716              * @param {ColorPalette} this
25717              * @param {String} color The 6-digit color hex code (without the # symbol)
25718              */
25719         select: true
25720     });
25721
25722     if(this.handler){
25723         this.on("select", this.handler, this.scope, true);
25724     }
25725 };
25726 Roo.extend(Roo.ColorPalette, Roo.Component, {
25727     /**
25728      * @cfg {String} itemCls
25729      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25730      */
25731     itemCls : "x-color-palette",
25732     /**
25733      * @cfg {String} value
25734      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25735      * the hex codes are case-sensitive.
25736      */
25737     value : null,
25738     clickEvent:'click',
25739     // private
25740     ctype: "Roo.ColorPalette",
25741
25742     /**
25743      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25744      */
25745     allowReselect : false,
25746
25747     /**
25748      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25749      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25750      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25751      * of colors with the width setting until the box is symmetrical.</p>
25752      * <p>You can override individual colors if needed:</p>
25753      * <pre><code>
25754 var cp = new Roo.ColorPalette();
25755 cp.colors[0] = "FF0000";  // change the first box to red
25756 </code></pre>
25757
25758 Or you can provide a custom array of your own for complete control:
25759 <pre><code>
25760 var cp = new Roo.ColorPalette();
25761 cp.colors = ["000000", "993300", "333300"];
25762 </code></pre>
25763      * @type Array
25764      */
25765     colors : [
25766         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25767         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25768         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25769         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25770         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25771     ],
25772
25773     // private
25774     onRender : function(container, position){
25775         var t = new Roo.MasterTemplate(
25776             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25777         );
25778         var c = this.colors;
25779         for(var i = 0, len = c.length; i < len; i++){
25780             t.add([c[i]]);
25781         }
25782         var el = document.createElement("div");
25783         el.className = this.itemCls;
25784         t.overwrite(el);
25785         container.dom.insertBefore(el, position);
25786         this.el = Roo.get(el);
25787         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25788         if(this.clickEvent != 'click'){
25789             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25790         }
25791     },
25792
25793     // private
25794     afterRender : function(){
25795         Roo.ColorPalette.superclass.afterRender.call(this);
25796         if(this.value){
25797             var s = this.value;
25798             this.value = null;
25799             this.select(s);
25800         }
25801     },
25802
25803     // private
25804     handleClick : function(e, t){
25805         e.preventDefault();
25806         if(!this.disabled){
25807             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25808             this.select(c.toUpperCase());
25809         }
25810     },
25811
25812     /**
25813      * Selects the specified color in the palette (fires the select event)
25814      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25815      */
25816     select : function(color){
25817         color = color.replace("#", "");
25818         if(color != this.value || this.allowReselect){
25819             var el = this.el;
25820             if(this.value){
25821                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25822             }
25823             el.child("a.color-"+color).addClass("x-color-palette-sel");
25824             this.value = color;
25825             this.fireEvent("select", this, color);
25826         }
25827     }
25828 });/*
25829  * Based on:
25830  * Ext JS Library 1.1.1
25831  * Copyright(c) 2006-2007, Ext JS, LLC.
25832  *
25833  * Originally Released Under LGPL - original licence link has changed is not relivant.
25834  *
25835  * Fork - LGPL
25836  * <script type="text/javascript">
25837  */
25838  
25839 /**
25840  * @class Roo.DatePicker
25841  * @extends Roo.Component
25842  * Simple date picker class.
25843  * @constructor
25844  * Create a new DatePicker
25845  * @param {Object} config The config object
25846  */
25847 Roo.DatePicker = function(config){
25848     Roo.DatePicker.superclass.constructor.call(this, config);
25849
25850     this.value = config && config.value ?
25851                  config.value.clearTime() : new Date().clearTime();
25852
25853     this.addEvents({
25854         /**
25855              * @event select
25856              * Fires when a date is selected
25857              * @param {DatePicker} this
25858              * @param {Date} date The selected date
25859              */
25860         'select': true,
25861         /**
25862              * @event monthchange
25863              * Fires when the displayed month changes 
25864              * @param {DatePicker} this
25865              * @param {Date} date The selected month
25866              */
25867         'monthchange': true
25868     });
25869
25870     if(this.handler){
25871         this.on("select", this.handler,  this.scope || this);
25872     }
25873     // build the disabledDatesRE
25874     if(!this.disabledDatesRE && this.disabledDates){
25875         var dd = this.disabledDates;
25876         var re = "(?:";
25877         for(var i = 0; i < dd.length; i++){
25878             re += dd[i];
25879             if(i != dd.length-1) re += "|";
25880         }
25881         this.disabledDatesRE = new RegExp(re + ")");
25882     }
25883 };
25884
25885 Roo.extend(Roo.DatePicker, Roo.Component, {
25886     /**
25887      * @cfg {String} todayText
25888      * The text to display on the button that selects the current date (defaults to "Today")
25889      */
25890     todayText : "Today",
25891     /**
25892      * @cfg {String} okText
25893      * The text to display on the ok button
25894      */
25895     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25896     /**
25897      * @cfg {String} cancelText
25898      * The text to display on the cancel button
25899      */
25900     cancelText : "Cancel",
25901     /**
25902      * @cfg {String} todayTip
25903      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25904      */
25905     todayTip : "{0} (Spacebar)",
25906     /**
25907      * @cfg {Date} minDate
25908      * Minimum allowable date (JavaScript date object, defaults to null)
25909      */
25910     minDate : null,
25911     /**
25912      * @cfg {Date} maxDate
25913      * Maximum allowable date (JavaScript date object, defaults to null)
25914      */
25915     maxDate : null,
25916     /**
25917      * @cfg {String} minText
25918      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25919      */
25920     minText : "This date is before the minimum date",
25921     /**
25922      * @cfg {String} maxText
25923      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25924      */
25925     maxText : "This date is after the maximum date",
25926     /**
25927      * @cfg {String} format
25928      * The default date format string which can be overriden for localization support.  The format must be
25929      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25930      */
25931     format : "m/d/y",
25932     /**
25933      * @cfg {Array} disabledDays
25934      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25935      */
25936     disabledDays : null,
25937     /**
25938      * @cfg {String} disabledDaysText
25939      * The tooltip to display when the date falls on a disabled day (defaults to "")
25940      */
25941     disabledDaysText : "",
25942     /**
25943      * @cfg {RegExp} disabledDatesRE
25944      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25945      */
25946     disabledDatesRE : null,
25947     /**
25948      * @cfg {String} disabledDatesText
25949      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25950      */
25951     disabledDatesText : "",
25952     /**
25953      * @cfg {Boolean} constrainToViewport
25954      * True to constrain the date picker to the viewport (defaults to true)
25955      */
25956     constrainToViewport : true,
25957     /**
25958      * @cfg {Array} monthNames
25959      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25960      */
25961     monthNames : Date.monthNames,
25962     /**
25963      * @cfg {Array} dayNames
25964      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25965      */
25966     dayNames : Date.dayNames,
25967     /**
25968      * @cfg {String} nextText
25969      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25970      */
25971     nextText: 'Next Month (Control+Right)',
25972     /**
25973      * @cfg {String} prevText
25974      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25975      */
25976     prevText: 'Previous Month (Control+Left)',
25977     /**
25978      * @cfg {String} monthYearText
25979      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25980      */
25981     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25982     /**
25983      * @cfg {Number} startDay
25984      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25985      */
25986     startDay : 0,
25987     /**
25988      * @cfg {Bool} showClear
25989      * Show a clear button (usefull for date form elements that can be blank.)
25990      */
25991     
25992     showClear: false,
25993     
25994     /**
25995      * Sets the value of the date field
25996      * @param {Date} value The date to set
25997      */
25998     setValue : function(value){
25999         var old = this.value;
26000         
26001         if (typeof(value) == 'string') {
26002          
26003             value = Date.parseDate(value, this.format);
26004         }
26005         if (!value) {
26006             value = new Date();
26007         }
26008         
26009         this.value = value.clearTime(true);
26010         if(this.el){
26011             this.update(this.value);
26012         }
26013     },
26014
26015     /**
26016      * Gets the current selected value of the date field
26017      * @return {Date} The selected date
26018      */
26019     getValue : function(){
26020         return this.value;
26021     },
26022
26023     // private
26024     focus : function(){
26025         if(this.el){
26026             this.update(this.activeDate);
26027         }
26028     },
26029
26030     // privateval
26031     onRender : function(container, position){
26032         
26033         var m = [
26034              '<table cellspacing="0">',
26035                 '<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>',
26036                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26037         var dn = this.dayNames;
26038         for(var i = 0; i < 7; i++){
26039             var d = this.startDay+i;
26040             if(d > 6){
26041                 d = d-7;
26042             }
26043             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26044         }
26045         m[m.length] = "</tr></thead><tbody><tr>";
26046         for(var i = 0; i < 42; i++) {
26047             if(i % 7 == 0 && i != 0){
26048                 m[m.length] = "</tr><tr>";
26049             }
26050             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26051         }
26052         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26053             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26054
26055         var el = document.createElement("div");
26056         el.className = "x-date-picker";
26057         el.innerHTML = m.join("");
26058
26059         container.dom.insertBefore(el, position);
26060
26061         this.el = Roo.get(el);
26062         this.eventEl = Roo.get(el.firstChild);
26063
26064         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26065             handler: this.showPrevMonth,
26066             scope: this,
26067             preventDefault:true,
26068             stopDefault:true
26069         });
26070
26071         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26072             handler: this.showNextMonth,
26073             scope: this,
26074             preventDefault:true,
26075             stopDefault:true
26076         });
26077
26078         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26079
26080         this.monthPicker = this.el.down('div.x-date-mp');
26081         this.monthPicker.enableDisplayMode('block');
26082         
26083         var kn = new Roo.KeyNav(this.eventEl, {
26084             "left" : function(e){
26085                 e.ctrlKey ?
26086                     this.showPrevMonth() :
26087                     this.update(this.activeDate.add("d", -1));
26088             },
26089
26090             "right" : function(e){
26091                 e.ctrlKey ?
26092                     this.showNextMonth() :
26093                     this.update(this.activeDate.add("d", 1));
26094             },
26095
26096             "up" : function(e){
26097                 e.ctrlKey ?
26098                     this.showNextYear() :
26099                     this.update(this.activeDate.add("d", -7));
26100             },
26101
26102             "down" : function(e){
26103                 e.ctrlKey ?
26104                     this.showPrevYear() :
26105                     this.update(this.activeDate.add("d", 7));
26106             },
26107
26108             "pageUp" : function(e){
26109                 this.showNextMonth();
26110             },
26111
26112             "pageDown" : function(e){
26113                 this.showPrevMonth();
26114             },
26115
26116             "enter" : function(e){
26117                 e.stopPropagation();
26118                 return true;
26119             },
26120
26121             scope : this
26122         });
26123
26124         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26125
26126         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26127
26128         this.el.unselectable();
26129         
26130         this.cells = this.el.select("table.x-date-inner tbody td");
26131         this.textNodes = this.el.query("table.x-date-inner tbody span");
26132
26133         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26134             text: "&#160;",
26135             tooltip: this.monthYearText
26136         });
26137
26138         this.mbtn.on('click', this.showMonthPicker, this);
26139         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26140
26141
26142         var today = (new Date()).dateFormat(this.format);
26143         
26144         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26145         if (this.showClear) {
26146             baseTb.add( new Roo.Toolbar.Fill());
26147         }
26148         baseTb.add({
26149             text: String.format(this.todayText, today),
26150             tooltip: String.format(this.todayTip, today),
26151             handler: this.selectToday,
26152             scope: this
26153         });
26154         
26155         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26156             
26157         //});
26158         if (this.showClear) {
26159             
26160             baseTb.add( new Roo.Toolbar.Fill());
26161             baseTb.add({
26162                 text: '&#160;',
26163                 cls: 'x-btn-icon x-btn-clear',
26164                 handler: function() {
26165                     //this.value = '';
26166                     this.fireEvent("select", this, '');
26167                 },
26168                 scope: this
26169             });
26170         }
26171         
26172         
26173         if(Roo.isIE){
26174             this.el.repaint();
26175         }
26176         this.update(this.value);
26177     },
26178
26179     createMonthPicker : function(){
26180         if(!this.monthPicker.dom.firstChild){
26181             var buf = ['<table border="0" cellspacing="0">'];
26182             for(var i = 0; i < 6; i++){
26183                 buf.push(
26184                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26185                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26186                     i == 0 ?
26187                     '<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>' :
26188                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26189                 );
26190             }
26191             buf.push(
26192                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26193                     this.okText,
26194                     '</button><button type="button" class="x-date-mp-cancel">',
26195                     this.cancelText,
26196                     '</button></td></tr>',
26197                 '</table>'
26198             );
26199             this.monthPicker.update(buf.join(''));
26200             this.monthPicker.on('click', this.onMonthClick, this);
26201             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26202
26203             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26204             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26205
26206             this.mpMonths.each(function(m, a, i){
26207                 i += 1;
26208                 if((i%2) == 0){
26209                     m.dom.xmonth = 5 + Math.round(i * .5);
26210                 }else{
26211                     m.dom.xmonth = Math.round((i-1) * .5);
26212                 }
26213             });
26214         }
26215     },
26216
26217     showMonthPicker : function(){
26218         this.createMonthPicker();
26219         var size = this.el.getSize();
26220         this.monthPicker.setSize(size);
26221         this.monthPicker.child('table').setSize(size);
26222
26223         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26224         this.updateMPMonth(this.mpSelMonth);
26225         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26226         this.updateMPYear(this.mpSelYear);
26227
26228         this.monthPicker.slideIn('t', {duration:.2});
26229     },
26230
26231     updateMPYear : function(y){
26232         this.mpyear = y;
26233         var ys = this.mpYears.elements;
26234         for(var i = 1; i <= 10; i++){
26235             var td = ys[i-1], y2;
26236             if((i%2) == 0){
26237                 y2 = y + Math.round(i * .5);
26238                 td.firstChild.innerHTML = y2;
26239                 td.xyear = y2;
26240             }else{
26241                 y2 = y - (5-Math.round(i * .5));
26242                 td.firstChild.innerHTML = y2;
26243                 td.xyear = y2;
26244             }
26245             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26246         }
26247     },
26248
26249     updateMPMonth : function(sm){
26250         this.mpMonths.each(function(m, a, i){
26251             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26252         });
26253     },
26254
26255     selectMPMonth: function(m){
26256         
26257     },
26258
26259     onMonthClick : function(e, t){
26260         e.stopEvent();
26261         var el = new Roo.Element(t), pn;
26262         if(el.is('button.x-date-mp-cancel')){
26263             this.hideMonthPicker();
26264         }
26265         else if(el.is('button.x-date-mp-ok')){
26266             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26267             this.hideMonthPicker();
26268         }
26269         else if(pn = el.up('td.x-date-mp-month', 2)){
26270             this.mpMonths.removeClass('x-date-mp-sel');
26271             pn.addClass('x-date-mp-sel');
26272             this.mpSelMonth = pn.dom.xmonth;
26273         }
26274         else if(pn = el.up('td.x-date-mp-year', 2)){
26275             this.mpYears.removeClass('x-date-mp-sel');
26276             pn.addClass('x-date-mp-sel');
26277             this.mpSelYear = pn.dom.xyear;
26278         }
26279         else if(el.is('a.x-date-mp-prev')){
26280             this.updateMPYear(this.mpyear-10);
26281         }
26282         else if(el.is('a.x-date-mp-next')){
26283             this.updateMPYear(this.mpyear+10);
26284         }
26285     },
26286
26287     onMonthDblClick : function(e, t){
26288         e.stopEvent();
26289         var el = new Roo.Element(t), pn;
26290         if(pn = el.up('td.x-date-mp-month', 2)){
26291             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26292             this.hideMonthPicker();
26293         }
26294         else if(pn = el.up('td.x-date-mp-year', 2)){
26295             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26296             this.hideMonthPicker();
26297         }
26298     },
26299
26300     hideMonthPicker : function(disableAnim){
26301         if(this.monthPicker){
26302             if(disableAnim === true){
26303                 this.monthPicker.hide();
26304             }else{
26305                 this.monthPicker.slideOut('t', {duration:.2});
26306             }
26307         }
26308     },
26309
26310     // private
26311     showPrevMonth : function(e){
26312         this.update(this.activeDate.add("mo", -1));
26313     },
26314
26315     // private
26316     showNextMonth : function(e){
26317         this.update(this.activeDate.add("mo", 1));
26318     },
26319
26320     // private
26321     showPrevYear : function(){
26322         this.update(this.activeDate.add("y", -1));
26323     },
26324
26325     // private
26326     showNextYear : function(){
26327         this.update(this.activeDate.add("y", 1));
26328     },
26329
26330     // private
26331     handleMouseWheel : function(e){
26332         var delta = e.getWheelDelta();
26333         if(delta > 0){
26334             this.showPrevMonth();
26335             e.stopEvent();
26336         } else if(delta < 0){
26337             this.showNextMonth();
26338             e.stopEvent();
26339         }
26340     },
26341
26342     // private
26343     handleDateClick : function(e, t){
26344         e.stopEvent();
26345         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26346             this.setValue(new Date(t.dateValue));
26347             this.fireEvent("select", this, this.value);
26348         }
26349     },
26350
26351     // private
26352     selectToday : function(){
26353         this.setValue(new Date().clearTime());
26354         this.fireEvent("select", this, this.value);
26355     },
26356
26357     // private
26358     update : function(date)
26359     {
26360         var vd = this.activeDate;
26361         this.activeDate = date;
26362         if(vd && this.el){
26363             var t = date.getTime();
26364             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26365                 this.cells.removeClass("x-date-selected");
26366                 this.cells.each(function(c){
26367                    if(c.dom.firstChild.dateValue == t){
26368                        c.addClass("x-date-selected");
26369                        setTimeout(function(){
26370                             try{c.dom.firstChild.focus();}catch(e){}
26371                        }, 50);
26372                        return false;
26373                    }
26374                 });
26375                 return;
26376             }
26377         }
26378         
26379         var days = date.getDaysInMonth();
26380         var firstOfMonth = date.getFirstDateOfMonth();
26381         var startingPos = firstOfMonth.getDay()-this.startDay;
26382
26383         if(startingPos <= this.startDay){
26384             startingPos += 7;
26385         }
26386
26387         var pm = date.add("mo", -1);
26388         var prevStart = pm.getDaysInMonth()-startingPos;
26389
26390         var cells = this.cells.elements;
26391         var textEls = this.textNodes;
26392         days += startingPos;
26393
26394         // convert everything to numbers so it's fast
26395         var day = 86400000;
26396         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26397         var today = new Date().clearTime().getTime();
26398         var sel = date.clearTime().getTime();
26399         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26400         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26401         var ddMatch = this.disabledDatesRE;
26402         var ddText = this.disabledDatesText;
26403         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26404         var ddaysText = this.disabledDaysText;
26405         var format = this.format;
26406
26407         var setCellClass = function(cal, cell){
26408             cell.title = "";
26409             var t = d.getTime();
26410             cell.firstChild.dateValue = t;
26411             if(t == today){
26412                 cell.className += " x-date-today";
26413                 cell.title = cal.todayText;
26414             }
26415             if(t == sel){
26416                 cell.className += " x-date-selected";
26417                 setTimeout(function(){
26418                     try{cell.firstChild.focus();}catch(e){}
26419                 }, 50);
26420             }
26421             // disabling
26422             if(t < min) {
26423                 cell.className = " x-date-disabled";
26424                 cell.title = cal.minText;
26425                 return;
26426             }
26427             if(t > max) {
26428                 cell.className = " x-date-disabled";
26429                 cell.title = cal.maxText;
26430                 return;
26431             }
26432             if(ddays){
26433                 if(ddays.indexOf(d.getDay()) != -1){
26434                     cell.title = ddaysText;
26435                     cell.className = " x-date-disabled";
26436                 }
26437             }
26438             if(ddMatch && format){
26439                 var fvalue = d.dateFormat(format);
26440                 if(ddMatch.test(fvalue)){
26441                     cell.title = ddText.replace("%0", fvalue);
26442                     cell.className = " x-date-disabled";
26443                 }
26444             }
26445         };
26446
26447         var i = 0;
26448         for(; i < startingPos; i++) {
26449             textEls[i].innerHTML = (++prevStart);
26450             d.setDate(d.getDate()+1);
26451             cells[i].className = "x-date-prevday";
26452             setCellClass(this, cells[i]);
26453         }
26454         for(; i < days; i++){
26455             intDay = i - startingPos + 1;
26456             textEls[i].innerHTML = (intDay);
26457             d.setDate(d.getDate()+1);
26458             cells[i].className = "x-date-active";
26459             setCellClass(this, cells[i]);
26460         }
26461         var extraDays = 0;
26462         for(; i < 42; i++) {
26463              textEls[i].innerHTML = (++extraDays);
26464              d.setDate(d.getDate()+1);
26465              cells[i].className = "x-date-nextday";
26466              setCellClass(this, cells[i]);
26467         }
26468
26469         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26470         this.fireEvent('monthchange', this, date);
26471         
26472         if(!this.internalRender){
26473             var main = this.el.dom.firstChild;
26474             var w = main.offsetWidth;
26475             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26476             Roo.fly(main).setWidth(w);
26477             this.internalRender = true;
26478             // opera does not respect the auto grow header center column
26479             // then, after it gets a width opera refuses to recalculate
26480             // without a second pass
26481             if(Roo.isOpera && !this.secondPass){
26482                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26483                 this.secondPass = true;
26484                 this.update.defer(10, this, [date]);
26485             }
26486         }
26487         
26488         
26489     }
26490 });        /*
26491  * Based on:
26492  * Ext JS Library 1.1.1
26493  * Copyright(c) 2006-2007, Ext JS, LLC.
26494  *
26495  * Originally Released Under LGPL - original licence link has changed is not relivant.
26496  *
26497  * Fork - LGPL
26498  * <script type="text/javascript">
26499  */
26500 /**
26501  * @class Roo.TabPanel
26502  * @extends Roo.util.Observable
26503  * A lightweight tab container.
26504  * <br><br>
26505  * Usage:
26506  * <pre><code>
26507 // basic tabs 1, built from existing content
26508 var tabs = new Roo.TabPanel("tabs1");
26509 tabs.addTab("script", "View Script");
26510 tabs.addTab("markup", "View Markup");
26511 tabs.activate("script");
26512
26513 // more advanced tabs, built from javascript
26514 var jtabs = new Roo.TabPanel("jtabs");
26515 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26516
26517 // set up the UpdateManager
26518 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26519 var updater = tab2.getUpdateManager();
26520 updater.setDefaultUrl("ajax1.htm");
26521 tab2.on('activate', updater.refresh, updater, true);
26522
26523 // Use setUrl for Ajax loading
26524 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26525 tab3.setUrl("ajax2.htm", null, true);
26526
26527 // Disabled tab
26528 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26529 tab4.disable();
26530
26531 jtabs.activate("jtabs-1");
26532  * </code></pre>
26533  * @constructor
26534  * Create a new TabPanel.
26535  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26536  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26537  */
26538 Roo.TabPanel = function(container, config){
26539     /**
26540     * The container element for this TabPanel.
26541     * @type Roo.Element
26542     */
26543     this.el = Roo.get(container, true);
26544     if(config){
26545         if(typeof config == "boolean"){
26546             this.tabPosition = config ? "bottom" : "top";
26547         }else{
26548             Roo.apply(this, config);
26549         }
26550     }
26551     if(this.tabPosition == "bottom"){
26552         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26553         this.el.addClass("x-tabs-bottom");
26554     }
26555     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26556     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26557     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26558     if(Roo.isIE){
26559         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26560     }
26561     if(this.tabPosition != "bottom"){
26562         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26563          * @type Roo.Element
26564          */
26565         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26566         this.el.addClass("x-tabs-top");
26567     }
26568     this.items = [];
26569
26570     this.bodyEl.setStyle("position", "relative");
26571
26572     this.active = null;
26573     this.activateDelegate = this.activate.createDelegate(this);
26574
26575     this.addEvents({
26576         /**
26577          * @event tabchange
26578          * Fires when the active tab changes
26579          * @param {Roo.TabPanel} this
26580          * @param {Roo.TabPanelItem} activePanel The new active tab
26581          */
26582         "tabchange": true,
26583         /**
26584          * @event beforetabchange
26585          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26586          * @param {Roo.TabPanel} this
26587          * @param {Object} e Set cancel to true on this object to cancel the tab change
26588          * @param {Roo.TabPanelItem} tab The tab being changed to
26589          */
26590         "beforetabchange" : true
26591     });
26592
26593     Roo.EventManager.onWindowResize(this.onResize, this);
26594     this.cpad = this.el.getPadding("lr");
26595     this.hiddenCount = 0;
26596
26597
26598     // toolbar on the tabbar support...
26599     if (this.toolbar) {
26600         var tcfg = this.toolbar;
26601         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26602         this.toolbar = new Roo.Toolbar(tcfg);
26603         if (Roo.isSafari) {
26604             var tbl = tcfg.container.child('table', true);
26605             tbl.setAttribute('width', '100%');
26606         }
26607         
26608     }
26609    
26610
26611
26612     Roo.TabPanel.superclass.constructor.call(this);
26613 };
26614
26615 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26616     /*
26617      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26618      */
26619     tabPosition : "top",
26620     /*
26621      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26622      */
26623     currentTabWidth : 0,
26624     /*
26625      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26626      */
26627     minTabWidth : 40,
26628     /*
26629      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26630      */
26631     maxTabWidth : 250,
26632     /*
26633      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26634      */
26635     preferredTabWidth : 175,
26636     /*
26637      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26638      */
26639     resizeTabs : false,
26640     /*
26641      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26642      */
26643     monitorResize : true,
26644     /*
26645      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26646      */
26647     toolbar : false,
26648
26649     /**
26650      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26651      * @param {String} id The id of the div to use <b>or create</b>
26652      * @param {String} text The text for the tab
26653      * @param {String} content (optional) Content to put in the TabPanelItem body
26654      * @param {Boolean} closable (optional) True to create a close icon on the tab
26655      * @return {Roo.TabPanelItem} The created TabPanelItem
26656      */
26657     addTab : function(id, text, content, closable){
26658         var item = new Roo.TabPanelItem(this, id, text, closable);
26659         this.addTabItem(item);
26660         if(content){
26661             item.setContent(content);
26662         }
26663         return item;
26664     },
26665
26666     /**
26667      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26668      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26669      * @return {Roo.TabPanelItem}
26670      */
26671     getTab : function(id){
26672         return this.items[id];
26673     },
26674
26675     /**
26676      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26677      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26678      */
26679     hideTab : function(id){
26680         var t = this.items[id];
26681         if(!t.isHidden()){
26682            t.setHidden(true);
26683            this.hiddenCount++;
26684            this.autoSizeTabs();
26685         }
26686     },
26687
26688     /**
26689      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26690      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26691      */
26692     unhideTab : function(id){
26693         var t = this.items[id];
26694         if(t.isHidden()){
26695            t.setHidden(false);
26696            this.hiddenCount--;
26697            this.autoSizeTabs();
26698         }
26699     },
26700
26701     /**
26702      * Adds an existing {@link Roo.TabPanelItem}.
26703      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26704      */
26705     addTabItem : function(item){
26706         this.items[item.id] = item;
26707         this.items.push(item);
26708         if(this.resizeTabs){
26709            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26710            this.autoSizeTabs();
26711         }else{
26712             item.autoSize();
26713         }
26714     },
26715
26716     /**
26717      * Removes a {@link Roo.TabPanelItem}.
26718      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26719      */
26720     removeTab : function(id){
26721         var items = this.items;
26722         var tab = items[id];
26723         if(!tab) { return; }
26724         var index = items.indexOf(tab);
26725         if(this.active == tab && items.length > 1){
26726             var newTab = this.getNextAvailable(index);
26727             if(newTab) {
26728                 newTab.activate();
26729             }
26730         }
26731         this.stripEl.dom.removeChild(tab.pnode.dom);
26732         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26733             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26734         }
26735         items.splice(index, 1);
26736         delete this.items[tab.id];
26737         tab.fireEvent("close", tab);
26738         tab.purgeListeners();
26739         this.autoSizeTabs();
26740     },
26741
26742     getNextAvailable : function(start){
26743         var items = this.items;
26744         var index = start;
26745         // look for a next tab that will slide over to
26746         // replace the one being removed
26747         while(index < items.length){
26748             var item = items[++index];
26749             if(item && !item.isHidden()){
26750                 return item;
26751             }
26752         }
26753         // if one isn't found select the previous tab (on the left)
26754         index = start;
26755         while(index >= 0){
26756             var item = items[--index];
26757             if(item && !item.isHidden()){
26758                 return item;
26759             }
26760         }
26761         return null;
26762     },
26763
26764     /**
26765      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26766      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26767      */
26768     disableTab : function(id){
26769         var tab = this.items[id];
26770         if(tab && this.active != tab){
26771             tab.disable();
26772         }
26773     },
26774
26775     /**
26776      * Enables a {@link Roo.TabPanelItem} that is disabled.
26777      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26778      */
26779     enableTab : function(id){
26780         var tab = this.items[id];
26781         tab.enable();
26782     },
26783
26784     /**
26785      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26786      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26787      * @return {Roo.TabPanelItem} The TabPanelItem.
26788      */
26789     activate : function(id){
26790         var tab = this.items[id];
26791         if(!tab){
26792             return null;
26793         }
26794         if(tab == this.active || tab.disabled){
26795             return tab;
26796         }
26797         var e = {};
26798         this.fireEvent("beforetabchange", this, e, tab);
26799         if(e.cancel !== true && !tab.disabled){
26800             if(this.active){
26801                 this.active.hide();
26802             }
26803             this.active = this.items[id];
26804             this.active.show();
26805             this.fireEvent("tabchange", this, this.active);
26806         }
26807         return tab;
26808     },
26809
26810     /**
26811      * Gets the active {@link Roo.TabPanelItem}.
26812      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26813      */
26814     getActiveTab : function(){
26815         return this.active;
26816     },
26817
26818     /**
26819      * Updates the tab body element to fit the height of the container element
26820      * for overflow scrolling
26821      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26822      */
26823     syncHeight : function(targetHeight){
26824         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26825         var bm = this.bodyEl.getMargins();
26826         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26827         this.bodyEl.setHeight(newHeight);
26828         return newHeight;
26829     },
26830
26831     onResize : function(){
26832         if(this.monitorResize){
26833             this.autoSizeTabs();
26834         }
26835     },
26836
26837     /**
26838      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26839      */
26840     beginUpdate : function(){
26841         this.updating = true;
26842     },
26843
26844     /**
26845      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26846      */
26847     endUpdate : function(){
26848         this.updating = false;
26849         this.autoSizeTabs();
26850     },
26851
26852     /**
26853      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26854      */
26855     autoSizeTabs : function(){
26856         var count = this.items.length;
26857         var vcount = count - this.hiddenCount;
26858         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26859         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26860         var availWidth = Math.floor(w / vcount);
26861         var b = this.stripBody;
26862         if(b.getWidth() > w){
26863             var tabs = this.items;
26864             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26865             if(availWidth < this.minTabWidth){
26866                 /*if(!this.sleft){    // incomplete scrolling code
26867                     this.createScrollButtons();
26868                 }
26869                 this.showScroll();
26870                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26871             }
26872         }else{
26873             if(this.currentTabWidth < this.preferredTabWidth){
26874                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26875             }
26876         }
26877     },
26878
26879     /**
26880      * Returns the number of tabs in this TabPanel.
26881      * @return {Number}
26882      */
26883      getCount : function(){
26884          return this.items.length;
26885      },
26886
26887     /**
26888      * Resizes all the tabs to the passed width
26889      * @param {Number} The new width
26890      */
26891     setTabWidth : function(width){
26892         this.currentTabWidth = width;
26893         for(var i = 0, len = this.items.length; i < len; i++) {
26894                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26895         }
26896     },
26897
26898     /**
26899      * Destroys this TabPanel
26900      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26901      */
26902     destroy : function(removeEl){
26903         Roo.EventManager.removeResizeListener(this.onResize, this);
26904         for(var i = 0, len = this.items.length; i < len; i++){
26905             this.items[i].purgeListeners();
26906         }
26907         if(removeEl === true){
26908             this.el.update("");
26909             this.el.remove();
26910         }
26911     }
26912 });
26913
26914 /**
26915  * @class Roo.TabPanelItem
26916  * @extends Roo.util.Observable
26917  * Represents an individual item (tab plus body) in a TabPanel.
26918  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26919  * @param {String} id The id of this TabPanelItem
26920  * @param {String} text The text for the tab of this TabPanelItem
26921  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26922  */
26923 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26924     /**
26925      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26926      * @type Roo.TabPanel
26927      */
26928     this.tabPanel = tabPanel;
26929     /**
26930      * The id for this TabPanelItem
26931      * @type String
26932      */
26933     this.id = id;
26934     /** @private */
26935     this.disabled = false;
26936     /** @private */
26937     this.text = text;
26938     /** @private */
26939     this.loaded = false;
26940     this.closable = closable;
26941
26942     /**
26943      * The body element for this TabPanelItem.
26944      * @type Roo.Element
26945      */
26946     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26947     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26948     this.bodyEl.setStyle("display", "block");
26949     this.bodyEl.setStyle("zoom", "1");
26950     this.hideAction();
26951
26952     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26953     /** @private */
26954     this.el = Roo.get(els.el, true);
26955     this.inner = Roo.get(els.inner, true);
26956     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26957     this.pnode = Roo.get(els.el.parentNode, true);
26958     this.el.on("mousedown", this.onTabMouseDown, this);
26959     this.el.on("click", this.onTabClick, this);
26960     /** @private */
26961     if(closable){
26962         var c = Roo.get(els.close, true);
26963         c.dom.title = this.closeText;
26964         c.addClassOnOver("close-over");
26965         c.on("click", this.closeClick, this);
26966      }
26967
26968     this.addEvents({
26969          /**
26970          * @event activate
26971          * Fires when this tab becomes the active tab.
26972          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26973          * @param {Roo.TabPanelItem} this
26974          */
26975         "activate": true,
26976         /**
26977          * @event beforeclose
26978          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26979          * @param {Roo.TabPanelItem} this
26980          * @param {Object} e Set cancel to true on this object to cancel the close.
26981          */
26982         "beforeclose": true,
26983         /**
26984          * @event close
26985          * Fires when this tab is closed.
26986          * @param {Roo.TabPanelItem} this
26987          */
26988          "close": true,
26989         /**
26990          * @event deactivate
26991          * Fires when this tab is no longer the active tab.
26992          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26993          * @param {Roo.TabPanelItem} this
26994          */
26995          "deactivate" : true
26996     });
26997     this.hidden = false;
26998
26999     Roo.TabPanelItem.superclass.constructor.call(this);
27000 };
27001
27002 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27003     purgeListeners : function(){
27004        Roo.util.Observable.prototype.purgeListeners.call(this);
27005        this.el.removeAllListeners();
27006     },
27007     /**
27008      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27009      */
27010     show : function(){
27011         this.pnode.addClass("on");
27012         this.showAction();
27013         if(Roo.isOpera){
27014             this.tabPanel.stripWrap.repaint();
27015         }
27016         this.fireEvent("activate", this.tabPanel, this);
27017     },
27018
27019     /**
27020      * Returns true if this tab is the active tab.
27021      * @return {Boolean}
27022      */
27023     isActive : function(){
27024         return this.tabPanel.getActiveTab() == this;
27025     },
27026
27027     /**
27028      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27029      */
27030     hide : function(){
27031         this.pnode.removeClass("on");
27032         this.hideAction();
27033         this.fireEvent("deactivate", this.tabPanel, this);
27034     },
27035
27036     hideAction : function(){
27037         this.bodyEl.hide();
27038         this.bodyEl.setStyle("position", "absolute");
27039         this.bodyEl.setLeft("-20000px");
27040         this.bodyEl.setTop("-20000px");
27041     },
27042
27043     showAction : function(){
27044         this.bodyEl.setStyle("position", "relative");
27045         this.bodyEl.setTop("");
27046         this.bodyEl.setLeft("");
27047         this.bodyEl.show();
27048     },
27049
27050     /**
27051      * Set the tooltip for the tab.
27052      * @param {String} tooltip The tab's tooltip
27053      */
27054     setTooltip : function(text){
27055         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27056             this.textEl.dom.qtip = text;
27057             this.textEl.dom.removeAttribute('title');
27058         }else{
27059             this.textEl.dom.title = text;
27060         }
27061     },
27062
27063     onTabClick : function(e){
27064         e.preventDefault();
27065         this.tabPanel.activate(this.id);
27066     },
27067
27068     onTabMouseDown : function(e){
27069         e.preventDefault();
27070         this.tabPanel.activate(this.id);
27071     },
27072
27073     getWidth : function(){
27074         return this.inner.getWidth();
27075     },
27076
27077     setWidth : function(width){
27078         var iwidth = width - this.pnode.getPadding("lr");
27079         this.inner.setWidth(iwidth);
27080         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27081         this.pnode.setWidth(width);
27082     },
27083
27084     /**
27085      * Show or hide the tab
27086      * @param {Boolean} hidden True to hide or false to show.
27087      */
27088     setHidden : function(hidden){
27089         this.hidden = hidden;
27090         this.pnode.setStyle("display", hidden ? "none" : "");
27091     },
27092
27093     /**
27094      * Returns true if this tab is "hidden"
27095      * @return {Boolean}
27096      */
27097     isHidden : function(){
27098         return this.hidden;
27099     },
27100
27101     /**
27102      * Returns the text for this tab
27103      * @return {String}
27104      */
27105     getText : function(){
27106         return this.text;
27107     },
27108
27109     autoSize : function(){
27110         //this.el.beginMeasure();
27111         this.textEl.setWidth(1);
27112         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27113         //this.el.endMeasure();
27114     },
27115
27116     /**
27117      * Sets the text for the tab (Note: this also sets the tooltip text)
27118      * @param {String} text The tab's text and tooltip
27119      */
27120     setText : function(text){
27121         this.text = text;
27122         this.textEl.update(text);
27123         this.setTooltip(text);
27124         if(!this.tabPanel.resizeTabs){
27125             this.autoSize();
27126         }
27127     },
27128     /**
27129      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27130      */
27131     activate : function(){
27132         this.tabPanel.activate(this.id);
27133     },
27134
27135     /**
27136      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27137      */
27138     disable : function(){
27139         if(this.tabPanel.active != this){
27140             this.disabled = true;
27141             this.pnode.addClass("disabled");
27142         }
27143     },
27144
27145     /**
27146      * Enables this TabPanelItem if it was previously disabled.
27147      */
27148     enable : function(){
27149         this.disabled = false;
27150         this.pnode.removeClass("disabled");
27151     },
27152
27153     /**
27154      * Sets the content for this TabPanelItem.
27155      * @param {String} content The content
27156      * @param {Boolean} loadScripts true to look for and load scripts
27157      */
27158     setContent : function(content, loadScripts){
27159         this.bodyEl.update(content, loadScripts);
27160     },
27161
27162     /**
27163      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27164      * @return {Roo.UpdateManager} The UpdateManager
27165      */
27166     getUpdateManager : function(){
27167         return this.bodyEl.getUpdateManager();
27168     },
27169
27170     /**
27171      * Set a URL to be used to load the content for this TabPanelItem.
27172      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27173      * @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)
27174      * @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)
27175      * @return {Roo.UpdateManager} The UpdateManager
27176      */
27177     setUrl : function(url, params, loadOnce){
27178         if(this.refreshDelegate){
27179             this.un('activate', this.refreshDelegate);
27180         }
27181         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27182         this.on("activate", this.refreshDelegate);
27183         return this.bodyEl.getUpdateManager();
27184     },
27185
27186     /** @private */
27187     _handleRefresh : function(url, params, loadOnce){
27188         if(!loadOnce || !this.loaded){
27189             var updater = this.bodyEl.getUpdateManager();
27190             updater.update(url, params, this._setLoaded.createDelegate(this));
27191         }
27192     },
27193
27194     /**
27195      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27196      *   Will fail silently if the setUrl method has not been called.
27197      *   This does not activate the panel, just updates its content.
27198      */
27199     refresh : function(){
27200         if(this.refreshDelegate){
27201            this.loaded = false;
27202            this.refreshDelegate();
27203         }
27204     },
27205
27206     /** @private */
27207     _setLoaded : function(){
27208         this.loaded = true;
27209     },
27210
27211     /** @private */
27212     closeClick : function(e){
27213         var o = {};
27214         e.stopEvent();
27215         this.fireEvent("beforeclose", this, o);
27216         if(o.cancel !== true){
27217             this.tabPanel.removeTab(this.id);
27218         }
27219     },
27220     /**
27221      * The text displayed in the tooltip for the close icon.
27222      * @type String
27223      */
27224     closeText : "Close this tab"
27225 });
27226
27227 /** @private */
27228 Roo.TabPanel.prototype.createStrip = function(container){
27229     var strip = document.createElement("div");
27230     strip.className = "x-tabs-wrap";
27231     container.appendChild(strip);
27232     return strip;
27233 };
27234 /** @private */
27235 Roo.TabPanel.prototype.createStripList = function(strip){
27236     // div wrapper for retard IE
27237     // returns the "tr" element.
27238     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27239         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27240         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27241     return strip.firstChild.firstChild.firstChild.firstChild;
27242 };
27243 /** @private */
27244 Roo.TabPanel.prototype.createBody = function(container){
27245     var body = document.createElement("div");
27246     Roo.id(body, "tab-body");
27247     Roo.fly(body).addClass("x-tabs-body");
27248     container.appendChild(body);
27249     return body;
27250 };
27251 /** @private */
27252 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27253     var body = Roo.getDom(id);
27254     if(!body){
27255         body = document.createElement("div");
27256         body.id = id;
27257     }
27258     Roo.fly(body).addClass("x-tabs-item-body");
27259     bodyEl.insertBefore(body, bodyEl.firstChild);
27260     return body;
27261 };
27262 /** @private */
27263 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27264     var td = document.createElement("td");
27265     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27266     //stripEl.appendChild(td);
27267     if(closable){
27268         td.className = "x-tabs-closable";
27269         if(!this.closeTpl){
27270             this.closeTpl = new Roo.Template(
27271                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27272                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27273                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27274             );
27275         }
27276         var el = this.closeTpl.overwrite(td, {"text": text});
27277         var close = el.getElementsByTagName("div")[0];
27278         var inner = el.getElementsByTagName("em")[0];
27279         return {"el": el, "close": close, "inner": inner};
27280     } else {
27281         if(!this.tabTpl){
27282             this.tabTpl = new Roo.Template(
27283                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27284                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27285             );
27286         }
27287         var el = this.tabTpl.overwrite(td, {"text": text});
27288         var inner = el.getElementsByTagName("em")[0];
27289         return {"el": el, "inner": inner};
27290     }
27291 };/*
27292  * Based on:
27293  * Ext JS Library 1.1.1
27294  * Copyright(c) 2006-2007, Ext JS, LLC.
27295  *
27296  * Originally Released Under LGPL - original licence link has changed is not relivant.
27297  *
27298  * Fork - LGPL
27299  * <script type="text/javascript">
27300  */
27301
27302 /**
27303  * @class Roo.Button
27304  * @extends Roo.util.Observable
27305  * Simple Button class
27306  * @cfg {String} text The button text
27307  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27308  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27309  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27310  * @cfg {Object} scope The scope of the handler
27311  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27312  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27313  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27314  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27315  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27316  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27317    applies if enableToggle = true)
27318  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27319  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27320   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27321  * @constructor
27322  * Create a new button
27323  * @param {Object} config The config object
27324  */
27325 Roo.Button = function(renderTo, config)
27326 {
27327     if (!config) {
27328         config = renderTo;
27329         renderTo = config.renderTo || false;
27330     }
27331     
27332     Roo.apply(this, config);
27333     this.addEvents({
27334         /**
27335              * @event click
27336              * Fires when this button is clicked
27337              * @param {Button} this
27338              * @param {EventObject} e The click event
27339              */
27340             "click" : true,
27341         /**
27342              * @event toggle
27343              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27344              * @param {Button} this
27345              * @param {Boolean} pressed
27346              */
27347             "toggle" : true,
27348         /**
27349              * @event mouseover
27350              * Fires when the mouse hovers over the button
27351              * @param {Button} this
27352              * @param {Event} e The event object
27353              */
27354         'mouseover' : true,
27355         /**
27356              * @event mouseout
27357              * Fires when the mouse exits the button
27358              * @param {Button} this
27359              * @param {Event} e The event object
27360              */
27361         'mouseout': true,
27362          /**
27363              * @event render
27364              * Fires when the button is rendered
27365              * @param {Button} this
27366              */
27367         'render': true
27368     });
27369     if(this.menu){
27370         this.menu = Roo.menu.MenuMgr.get(this.menu);
27371     }
27372     // register listeners first!!  - so render can be captured..
27373     Roo.util.Observable.call(this);
27374     if(renderTo){
27375         this.render(renderTo);
27376     }
27377     
27378   
27379 };
27380
27381 Roo.extend(Roo.Button, Roo.util.Observable, {
27382     /**
27383      * 
27384      */
27385     
27386     /**
27387      * Read-only. True if this button is hidden
27388      * @type Boolean
27389      */
27390     hidden : false,
27391     /**
27392      * Read-only. True if this button is disabled
27393      * @type Boolean
27394      */
27395     disabled : false,
27396     /**
27397      * Read-only. True if this button is pressed (only if enableToggle = true)
27398      * @type Boolean
27399      */
27400     pressed : false,
27401
27402     /**
27403      * @cfg {Number} tabIndex 
27404      * The DOM tabIndex for this button (defaults to undefined)
27405      */
27406     tabIndex : undefined,
27407
27408     /**
27409      * @cfg {Boolean} enableToggle
27410      * True to enable pressed/not pressed toggling (defaults to false)
27411      */
27412     enableToggle: false,
27413     /**
27414      * @cfg {Mixed} menu
27415      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27416      */
27417     menu : undefined,
27418     /**
27419      * @cfg {String} menuAlign
27420      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27421      */
27422     menuAlign : "tl-bl?",
27423
27424     /**
27425      * @cfg {String} iconCls
27426      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27427      */
27428     iconCls : undefined,
27429     /**
27430      * @cfg {String} type
27431      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27432      */
27433     type : 'button',
27434
27435     // private
27436     menuClassTarget: 'tr',
27437
27438     /**
27439      * @cfg {String} clickEvent
27440      * The type of event to map to the button's event handler (defaults to 'click')
27441      */
27442     clickEvent : 'click',
27443
27444     /**
27445      * @cfg {Boolean} handleMouseEvents
27446      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27447      */
27448     handleMouseEvents : true,
27449
27450     /**
27451      * @cfg {String} tooltipType
27452      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27453      */
27454     tooltipType : 'qtip',
27455
27456     /**
27457      * @cfg {String} cls
27458      * A CSS class to apply to the button's main element.
27459      */
27460     
27461     /**
27462      * @cfg {Roo.Template} template (Optional)
27463      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27464      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27465      * require code modifications if required elements (e.g. a button) aren't present.
27466      */
27467
27468     // private
27469     render : function(renderTo){
27470         var btn;
27471         if(this.hideParent){
27472             this.parentEl = Roo.get(renderTo);
27473         }
27474         if(!this.dhconfig){
27475             if(!this.template){
27476                 if(!Roo.Button.buttonTemplate){
27477                     // hideous table template
27478                     Roo.Button.buttonTemplate = new Roo.Template(
27479                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27480                         '<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>',
27481                         "</tr></tbody></table>");
27482                 }
27483                 this.template = Roo.Button.buttonTemplate;
27484             }
27485             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27486             var btnEl = btn.child("button:first");
27487             btnEl.on('focus', this.onFocus, this);
27488             btnEl.on('blur', this.onBlur, this);
27489             if(this.cls){
27490                 btn.addClass(this.cls);
27491             }
27492             if(this.icon){
27493                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27494             }
27495             if(this.iconCls){
27496                 btnEl.addClass(this.iconCls);
27497                 if(!this.cls){
27498                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27499                 }
27500             }
27501             if(this.tabIndex !== undefined){
27502                 btnEl.dom.tabIndex = this.tabIndex;
27503             }
27504             if(this.tooltip){
27505                 if(typeof this.tooltip == 'object'){
27506                     Roo.QuickTips.tips(Roo.apply({
27507                           target: btnEl.id
27508                     }, this.tooltip));
27509                 } else {
27510                     btnEl.dom[this.tooltipType] = this.tooltip;
27511                 }
27512             }
27513         }else{
27514             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27515         }
27516         this.el = btn;
27517         if(this.id){
27518             this.el.dom.id = this.el.id = this.id;
27519         }
27520         if(this.menu){
27521             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27522             this.menu.on("show", this.onMenuShow, this);
27523             this.menu.on("hide", this.onMenuHide, this);
27524         }
27525         btn.addClass("x-btn");
27526         if(Roo.isIE && !Roo.isIE7){
27527             this.autoWidth.defer(1, this);
27528         }else{
27529             this.autoWidth();
27530         }
27531         if(this.handleMouseEvents){
27532             btn.on("mouseover", this.onMouseOver, this);
27533             btn.on("mouseout", this.onMouseOut, this);
27534             btn.on("mousedown", this.onMouseDown, this);
27535         }
27536         btn.on(this.clickEvent, this.onClick, this);
27537         //btn.on("mouseup", this.onMouseUp, this);
27538         if(this.hidden){
27539             this.hide();
27540         }
27541         if(this.disabled){
27542             this.disable();
27543         }
27544         Roo.ButtonToggleMgr.register(this);
27545         if(this.pressed){
27546             this.el.addClass("x-btn-pressed");
27547         }
27548         if(this.repeat){
27549             var repeater = new Roo.util.ClickRepeater(btn,
27550                 typeof this.repeat == "object" ? this.repeat : {}
27551             );
27552             repeater.on("click", this.onClick,  this);
27553         }
27554         
27555         this.fireEvent('render', this);
27556         
27557     },
27558     /**
27559      * Returns the button's underlying element
27560      * @return {Roo.Element} The element
27561      */
27562     getEl : function(){
27563         return this.el;  
27564     },
27565     
27566     /**
27567      * Destroys this Button and removes any listeners.
27568      */
27569     destroy : function(){
27570         Roo.ButtonToggleMgr.unregister(this);
27571         this.el.removeAllListeners();
27572         this.purgeListeners();
27573         this.el.remove();
27574     },
27575
27576     // private
27577     autoWidth : function(){
27578         if(this.el){
27579             this.el.setWidth("auto");
27580             if(Roo.isIE7 && Roo.isStrict){
27581                 var ib = this.el.child('button');
27582                 if(ib && ib.getWidth() > 20){
27583                     ib.clip();
27584                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27585                 }
27586             }
27587             if(this.minWidth){
27588                 if(this.hidden){
27589                     this.el.beginMeasure();
27590                 }
27591                 if(this.el.getWidth() < this.minWidth){
27592                     this.el.setWidth(this.minWidth);
27593                 }
27594                 if(this.hidden){
27595                     this.el.endMeasure();
27596                 }
27597             }
27598         }
27599     },
27600
27601     /**
27602      * Assigns this button's click handler
27603      * @param {Function} handler The function to call when the button is clicked
27604      * @param {Object} scope (optional) Scope for the function passed in
27605      */
27606     setHandler : function(handler, scope){
27607         this.handler = handler;
27608         this.scope = scope;  
27609     },
27610     
27611     /**
27612      * Sets this button's text
27613      * @param {String} text The button text
27614      */
27615     setText : function(text){
27616         this.text = text;
27617         if(this.el){
27618             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27619         }
27620         this.autoWidth();
27621     },
27622     
27623     /**
27624      * Gets the text for this button
27625      * @return {String} The button text
27626      */
27627     getText : function(){
27628         return this.text;  
27629     },
27630     
27631     /**
27632      * Show this button
27633      */
27634     show: function(){
27635         this.hidden = false;
27636         if(this.el){
27637             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27638         }
27639     },
27640     
27641     /**
27642      * Hide this button
27643      */
27644     hide: function(){
27645         this.hidden = true;
27646         if(this.el){
27647             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27648         }
27649     },
27650     
27651     /**
27652      * Convenience function for boolean show/hide
27653      * @param {Boolean} visible True to show, false to hide
27654      */
27655     setVisible: function(visible){
27656         if(visible) {
27657             this.show();
27658         }else{
27659             this.hide();
27660         }
27661     },
27662     
27663     /**
27664      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27665      * @param {Boolean} state (optional) Force a particular state
27666      */
27667     toggle : function(state){
27668         state = state === undefined ? !this.pressed : state;
27669         if(state != this.pressed){
27670             if(state){
27671                 this.el.addClass("x-btn-pressed");
27672                 this.pressed = true;
27673                 this.fireEvent("toggle", this, true);
27674             }else{
27675                 this.el.removeClass("x-btn-pressed");
27676                 this.pressed = false;
27677                 this.fireEvent("toggle", this, false);
27678             }
27679             if(this.toggleHandler){
27680                 this.toggleHandler.call(this.scope || this, this, state);
27681             }
27682         }
27683     },
27684     
27685     /**
27686      * Focus the button
27687      */
27688     focus : function(){
27689         this.el.child('button:first').focus();
27690     },
27691     
27692     /**
27693      * Disable this button
27694      */
27695     disable : function(){
27696         if(this.el){
27697             this.el.addClass("x-btn-disabled");
27698         }
27699         this.disabled = true;
27700     },
27701     
27702     /**
27703      * Enable this button
27704      */
27705     enable : function(){
27706         if(this.el){
27707             this.el.removeClass("x-btn-disabled");
27708         }
27709         this.disabled = false;
27710     },
27711
27712     /**
27713      * Convenience function for boolean enable/disable
27714      * @param {Boolean} enabled True to enable, false to disable
27715      */
27716     setDisabled : function(v){
27717         this[v !== true ? "enable" : "disable"]();
27718     },
27719
27720     // private
27721     onClick : function(e){
27722         if(e){
27723             e.preventDefault();
27724         }
27725         if(e.button != 0){
27726             return;
27727         }
27728         if(!this.disabled){
27729             if(this.enableToggle){
27730                 this.toggle();
27731             }
27732             if(this.menu && !this.menu.isVisible()){
27733                 this.menu.show(this.el, this.menuAlign);
27734             }
27735             this.fireEvent("click", this, e);
27736             if(this.handler){
27737                 this.el.removeClass("x-btn-over");
27738                 this.handler.call(this.scope || this, this, e);
27739             }
27740         }
27741     },
27742     // private
27743     onMouseOver : function(e){
27744         if(!this.disabled){
27745             this.el.addClass("x-btn-over");
27746             this.fireEvent('mouseover', this, e);
27747         }
27748     },
27749     // private
27750     onMouseOut : function(e){
27751         if(!e.within(this.el,  true)){
27752             this.el.removeClass("x-btn-over");
27753             this.fireEvent('mouseout', this, e);
27754         }
27755     },
27756     // private
27757     onFocus : function(e){
27758         if(!this.disabled){
27759             this.el.addClass("x-btn-focus");
27760         }
27761     },
27762     // private
27763     onBlur : function(e){
27764         this.el.removeClass("x-btn-focus");
27765     },
27766     // private
27767     onMouseDown : function(e){
27768         if(!this.disabled && e.button == 0){
27769             this.el.addClass("x-btn-click");
27770             Roo.get(document).on('mouseup', this.onMouseUp, this);
27771         }
27772     },
27773     // private
27774     onMouseUp : function(e){
27775         if(e.button == 0){
27776             this.el.removeClass("x-btn-click");
27777             Roo.get(document).un('mouseup', this.onMouseUp, this);
27778         }
27779     },
27780     // private
27781     onMenuShow : function(e){
27782         this.el.addClass("x-btn-menu-active");
27783     },
27784     // private
27785     onMenuHide : function(e){
27786         this.el.removeClass("x-btn-menu-active");
27787     }   
27788 });
27789
27790 // Private utility class used by Button
27791 Roo.ButtonToggleMgr = function(){
27792    var groups = {};
27793    
27794    function toggleGroup(btn, state){
27795        if(state){
27796            var g = groups[btn.toggleGroup];
27797            for(var i = 0, l = g.length; i < l; i++){
27798                if(g[i] != btn){
27799                    g[i].toggle(false);
27800                }
27801            }
27802        }
27803    }
27804    
27805    return {
27806        register : function(btn){
27807            if(!btn.toggleGroup){
27808                return;
27809            }
27810            var g = groups[btn.toggleGroup];
27811            if(!g){
27812                g = groups[btn.toggleGroup] = [];
27813            }
27814            g.push(btn);
27815            btn.on("toggle", toggleGroup);
27816        },
27817        
27818        unregister : function(btn){
27819            if(!btn.toggleGroup){
27820                return;
27821            }
27822            var g = groups[btn.toggleGroup];
27823            if(g){
27824                g.remove(btn);
27825                btn.un("toggle", toggleGroup);
27826            }
27827        }
27828    };
27829 }();/*
27830  * Based on:
27831  * Ext JS Library 1.1.1
27832  * Copyright(c) 2006-2007, Ext JS, LLC.
27833  *
27834  * Originally Released Under LGPL - original licence link has changed is not relivant.
27835  *
27836  * Fork - LGPL
27837  * <script type="text/javascript">
27838  */
27839  
27840 /**
27841  * @class Roo.SplitButton
27842  * @extends Roo.Button
27843  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27844  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27845  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27846  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27847  * @cfg {String} arrowTooltip The title attribute of the arrow
27848  * @constructor
27849  * Create a new menu button
27850  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27851  * @param {Object} config The config object
27852  */
27853 Roo.SplitButton = function(renderTo, config){
27854     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27855     /**
27856      * @event arrowclick
27857      * Fires when this button's arrow is clicked
27858      * @param {SplitButton} this
27859      * @param {EventObject} e The click event
27860      */
27861     this.addEvents({"arrowclick":true});
27862 };
27863
27864 Roo.extend(Roo.SplitButton, Roo.Button, {
27865     render : function(renderTo){
27866         // this is one sweet looking template!
27867         var tpl = new Roo.Template(
27868             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27869             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27870             '<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>',
27871             "</tbody></table></td><td>",
27872             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27873             '<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>',
27874             "</tbody></table></td></tr></table>"
27875         );
27876         var btn = tpl.append(renderTo, [this.text, this.type], true);
27877         var btnEl = btn.child("button");
27878         if(this.cls){
27879             btn.addClass(this.cls);
27880         }
27881         if(this.icon){
27882             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27883         }
27884         if(this.iconCls){
27885             btnEl.addClass(this.iconCls);
27886             if(!this.cls){
27887                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27888             }
27889         }
27890         this.el = btn;
27891         if(this.handleMouseEvents){
27892             btn.on("mouseover", this.onMouseOver, this);
27893             btn.on("mouseout", this.onMouseOut, this);
27894             btn.on("mousedown", this.onMouseDown, this);
27895             btn.on("mouseup", this.onMouseUp, this);
27896         }
27897         btn.on(this.clickEvent, this.onClick, this);
27898         if(this.tooltip){
27899             if(typeof this.tooltip == 'object'){
27900                 Roo.QuickTips.tips(Roo.apply({
27901                       target: btnEl.id
27902                 }, this.tooltip));
27903             } else {
27904                 btnEl.dom[this.tooltipType] = this.tooltip;
27905             }
27906         }
27907         if(this.arrowTooltip){
27908             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27909         }
27910         if(this.hidden){
27911             this.hide();
27912         }
27913         if(this.disabled){
27914             this.disable();
27915         }
27916         if(this.pressed){
27917             this.el.addClass("x-btn-pressed");
27918         }
27919         if(Roo.isIE && !Roo.isIE7){
27920             this.autoWidth.defer(1, this);
27921         }else{
27922             this.autoWidth();
27923         }
27924         if(this.menu){
27925             this.menu.on("show", this.onMenuShow, this);
27926             this.menu.on("hide", this.onMenuHide, this);
27927         }
27928         this.fireEvent('render', this);
27929     },
27930
27931     // private
27932     autoWidth : function(){
27933         if(this.el){
27934             var tbl = this.el.child("table:first");
27935             var tbl2 = this.el.child("table:last");
27936             this.el.setWidth("auto");
27937             tbl.setWidth("auto");
27938             if(Roo.isIE7 && Roo.isStrict){
27939                 var ib = this.el.child('button:first');
27940                 if(ib && ib.getWidth() > 20){
27941                     ib.clip();
27942                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27943                 }
27944             }
27945             if(this.minWidth){
27946                 if(this.hidden){
27947                     this.el.beginMeasure();
27948                 }
27949                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27950                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27951                 }
27952                 if(this.hidden){
27953                     this.el.endMeasure();
27954                 }
27955             }
27956             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27957         } 
27958     },
27959     /**
27960      * Sets this button's click handler
27961      * @param {Function} handler The function to call when the button is clicked
27962      * @param {Object} scope (optional) Scope for the function passed above
27963      */
27964     setHandler : function(handler, scope){
27965         this.handler = handler;
27966         this.scope = scope;  
27967     },
27968     
27969     /**
27970      * Sets this button's arrow click handler
27971      * @param {Function} handler The function to call when the arrow is clicked
27972      * @param {Object} scope (optional) Scope for the function passed above
27973      */
27974     setArrowHandler : function(handler, scope){
27975         this.arrowHandler = handler;
27976         this.scope = scope;  
27977     },
27978     
27979     /**
27980      * Focus the button
27981      */
27982     focus : function(){
27983         if(this.el){
27984             this.el.child("button:first").focus();
27985         }
27986     },
27987
27988     // private
27989     onClick : function(e){
27990         e.preventDefault();
27991         if(!this.disabled){
27992             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27993                 if(this.menu && !this.menu.isVisible()){
27994                     this.menu.show(this.el, this.menuAlign);
27995                 }
27996                 this.fireEvent("arrowclick", this, e);
27997                 if(this.arrowHandler){
27998                     this.arrowHandler.call(this.scope || this, this, e);
27999                 }
28000             }else{
28001                 this.fireEvent("click", this, e);
28002                 if(this.handler){
28003                     this.handler.call(this.scope || this, this, e);
28004                 }
28005             }
28006         }
28007     },
28008     // private
28009     onMouseDown : function(e){
28010         if(!this.disabled){
28011             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28012         }
28013     },
28014     // private
28015     onMouseUp : function(e){
28016         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28017     }   
28018 });
28019
28020
28021 // backwards compat
28022 Roo.MenuButton = Roo.SplitButton;/*
28023  * Based on:
28024  * Ext JS Library 1.1.1
28025  * Copyright(c) 2006-2007, Ext JS, LLC.
28026  *
28027  * Originally Released Under LGPL - original licence link has changed is not relivant.
28028  *
28029  * Fork - LGPL
28030  * <script type="text/javascript">
28031  */
28032
28033 /**
28034  * @class Roo.Toolbar
28035  * Basic Toolbar class.
28036  * @constructor
28037  * Creates a new Toolbar
28038  * @param {Object} container The config object
28039  */ 
28040 Roo.Toolbar = function(container, buttons, config)
28041 {
28042     /// old consturctor format still supported..
28043     if(container instanceof Array){ // omit the container for later rendering
28044         buttons = container;
28045         config = buttons;
28046         container = null;
28047     }
28048     if (typeof(container) == 'object' && container.xtype) {
28049         config = container;
28050         container = config.container;
28051         buttons = config.buttons || []; // not really - use items!!
28052     }
28053     var xitems = [];
28054     if (config && config.items) {
28055         xitems = config.items;
28056         delete config.items;
28057     }
28058     Roo.apply(this, config);
28059     this.buttons = buttons;
28060     
28061     if(container){
28062         this.render(container);
28063     }
28064     this.xitems = xitems;
28065     Roo.each(xitems, function(b) {
28066         this.add(b);
28067     }, this);
28068     
28069 };
28070
28071 Roo.Toolbar.prototype = {
28072     /**
28073      * @cfg {Array} items
28074      * array of button configs or elements to add (will be converted to a MixedCollection)
28075      */
28076     
28077     /**
28078      * @cfg {String/HTMLElement/Element} container
28079      * The id or element that will contain the toolbar
28080      */
28081     // private
28082     render : function(ct){
28083         this.el = Roo.get(ct);
28084         if(this.cls){
28085             this.el.addClass(this.cls);
28086         }
28087         // using a table allows for vertical alignment
28088         // 100% width is needed by Safari...
28089         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28090         this.tr = this.el.child("tr", true);
28091         var autoId = 0;
28092         this.items = new Roo.util.MixedCollection(false, function(o){
28093             return o.id || ("item" + (++autoId));
28094         });
28095         if(this.buttons){
28096             this.add.apply(this, this.buttons);
28097             delete this.buttons;
28098         }
28099     },
28100
28101     /**
28102      * Adds element(s) to the toolbar -- this function takes a variable number of 
28103      * arguments of mixed type and adds them to the toolbar.
28104      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28105      * <ul>
28106      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28107      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28108      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28109      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28110      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28111      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28112      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28113      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28114      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28115      * </ul>
28116      * @param {Mixed} arg2
28117      * @param {Mixed} etc.
28118      */
28119     add : function(){
28120         var a = arguments, l = a.length;
28121         for(var i = 0; i < l; i++){
28122             this._add(a[i]);
28123         }
28124     },
28125     // private..
28126     _add : function(el) {
28127         
28128         if (el.xtype) {
28129             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28130         }
28131         
28132         if (el.applyTo){ // some kind of form field
28133             return this.addField(el);
28134         } 
28135         if (el.render){ // some kind of Toolbar.Item
28136             return this.addItem(el);
28137         }
28138         if (typeof el == "string"){ // string
28139             if(el == "separator" || el == "-"){
28140                 return this.addSeparator();
28141             }
28142             if (el == " "){
28143                 return this.addSpacer();
28144             }
28145             if(el == "->"){
28146                 return this.addFill();
28147             }
28148             return this.addText(el);
28149             
28150         }
28151         if(el.tagName){ // element
28152             return this.addElement(el);
28153         }
28154         if(typeof el == "object"){ // must be button config?
28155             return this.addButton(el);
28156         }
28157         // and now what?!?!
28158         return false;
28159         
28160     },
28161     
28162     /**
28163      * Add an Xtype element
28164      * @param {Object} xtype Xtype Object
28165      * @return {Object} created Object
28166      */
28167     addxtype : function(e){
28168         return this.add(e);  
28169     },
28170     
28171     /**
28172      * Returns the Element for this toolbar.
28173      * @return {Roo.Element}
28174      */
28175     getEl : function(){
28176         return this.el;  
28177     },
28178     
28179     /**
28180      * Adds a separator
28181      * @return {Roo.Toolbar.Item} The separator item
28182      */
28183     addSeparator : function(){
28184         return this.addItem(new Roo.Toolbar.Separator());
28185     },
28186
28187     /**
28188      * Adds a spacer element
28189      * @return {Roo.Toolbar.Spacer} The spacer item
28190      */
28191     addSpacer : function(){
28192         return this.addItem(new Roo.Toolbar.Spacer());
28193     },
28194
28195     /**
28196      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28197      * @return {Roo.Toolbar.Fill} The fill item
28198      */
28199     addFill : function(){
28200         return this.addItem(new Roo.Toolbar.Fill());
28201     },
28202
28203     /**
28204      * Adds any standard HTML element to the toolbar
28205      * @param {String/HTMLElement/Element} el The element or id of the element to add
28206      * @return {Roo.Toolbar.Item} The element's item
28207      */
28208     addElement : function(el){
28209         return this.addItem(new Roo.Toolbar.Item(el));
28210     },
28211     /**
28212      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28213      * @type Roo.util.MixedCollection  
28214      */
28215     items : false,
28216      
28217     /**
28218      * Adds any Toolbar.Item or subclass
28219      * @param {Roo.Toolbar.Item} item
28220      * @return {Roo.Toolbar.Item} The item
28221      */
28222     addItem : function(item){
28223         var td = this.nextBlock();
28224         item.render(td);
28225         this.items.add(item);
28226         return item;
28227     },
28228     
28229     /**
28230      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28231      * @param {Object/Array} config A button config or array of configs
28232      * @return {Roo.Toolbar.Button/Array}
28233      */
28234     addButton : function(config){
28235         if(config instanceof Array){
28236             var buttons = [];
28237             for(var i = 0, len = config.length; i < len; i++) {
28238                 buttons.push(this.addButton(config[i]));
28239             }
28240             return buttons;
28241         }
28242         var b = config;
28243         if(!(config instanceof Roo.Toolbar.Button)){
28244             b = config.split ?
28245                 new Roo.Toolbar.SplitButton(config) :
28246                 new Roo.Toolbar.Button(config);
28247         }
28248         var td = this.nextBlock();
28249         b.render(td);
28250         this.items.add(b);
28251         return b;
28252     },
28253     
28254     /**
28255      * Adds text to the toolbar
28256      * @param {String} text The text to add
28257      * @return {Roo.Toolbar.Item} The element's item
28258      */
28259     addText : function(text){
28260         return this.addItem(new Roo.Toolbar.TextItem(text));
28261     },
28262     
28263     /**
28264      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28265      * @param {Number} index The index where the item is to be inserted
28266      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28267      * @return {Roo.Toolbar.Button/Item}
28268      */
28269     insertButton : function(index, item){
28270         if(item instanceof Array){
28271             var buttons = [];
28272             for(var i = 0, len = item.length; i < len; i++) {
28273                buttons.push(this.insertButton(index + i, item[i]));
28274             }
28275             return buttons;
28276         }
28277         if (!(item instanceof Roo.Toolbar.Button)){
28278            item = new Roo.Toolbar.Button(item);
28279         }
28280         var td = document.createElement("td");
28281         this.tr.insertBefore(td, this.tr.childNodes[index]);
28282         item.render(td);
28283         this.items.insert(index, item);
28284         return item;
28285     },
28286     
28287     /**
28288      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28289      * @param {Object} config
28290      * @return {Roo.Toolbar.Item} The element's item
28291      */
28292     addDom : function(config, returnEl){
28293         var td = this.nextBlock();
28294         Roo.DomHelper.overwrite(td, config);
28295         var ti = new Roo.Toolbar.Item(td.firstChild);
28296         ti.render(td);
28297         this.items.add(ti);
28298         return ti;
28299     },
28300
28301     /**
28302      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28303      * @type Roo.util.MixedCollection  
28304      */
28305     fields : false,
28306     
28307     /**
28308      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28309      * Note: the field should not have been rendered yet. For a field that has already been
28310      * rendered, use {@link #addElement}.
28311      * @param {Roo.form.Field} field
28312      * @return {Roo.ToolbarItem}
28313      */
28314      
28315       
28316     addField : function(field) {
28317         if (!this.fields) {
28318             var autoId = 0;
28319             this.fields = new Roo.util.MixedCollection(false, function(o){
28320                 return o.id || ("item" + (++autoId));
28321             });
28322
28323         }
28324         
28325         var td = this.nextBlock();
28326         field.render(td);
28327         var ti = new Roo.Toolbar.Item(td.firstChild);
28328         ti.render(td);
28329         this.items.add(ti);
28330         this.fields.add(field);
28331         return ti;
28332     },
28333     /**
28334      * Hide the toolbar
28335      * @method hide
28336      */
28337      
28338       
28339     hide : function()
28340     {
28341         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28342         this.el.child('div').hide();
28343     },
28344     /**
28345      * Show the toolbar
28346      * @method show
28347      */
28348     show : function()
28349     {
28350         this.el.child('div').show();
28351     },
28352       
28353     // private
28354     nextBlock : function(){
28355         var td = document.createElement("td");
28356         this.tr.appendChild(td);
28357         return td;
28358     },
28359
28360     // private
28361     destroy : function(){
28362         if(this.items){ // rendered?
28363             Roo.destroy.apply(Roo, this.items.items);
28364         }
28365         if(this.fields){ // rendered?
28366             Roo.destroy.apply(Roo, this.fields.items);
28367         }
28368         Roo.Element.uncache(this.el, this.tr);
28369     }
28370 };
28371
28372 /**
28373  * @class Roo.Toolbar.Item
28374  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28375  * @constructor
28376  * Creates a new Item
28377  * @param {HTMLElement} el 
28378  */
28379 Roo.Toolbar.Item = function(el){
28380     this.el = Roo.getDom(el);
28381     this.id = Roo.id(this.el);
28382     this.hidden = false;
28383 };
28384
28385 Roo.Toolbar.Item.prototype = {
28386     
28387     /**
28388      * Get this item's HTML Element
28389      * @return {HTMLElement}
28390      */
28391     getEl : function(){
28392        return this.el;  
28393     },
28394
28395     // private
28396     render : function(td){
28397         this.td = td;
28398         td.appendChild(this.el);
28399     },
28400     
28401     /**
28402      * Removes and destroys this item.
28403      */
28404     destroy : function(){
28405         this.td.parentNode.removeChild(this.td);
28406     },
28407     
28408     /**
28409      * Shows this item.
28410      */
28411     show: function(){
28412         this.hidden = false;
28413         this.td.style.display = "";
28414     },
28415     
28416     /**
28417      * Hides this item.
28418      */
28419     hide: function(){
28420         this.hidden = true;
28421         this.td.style.display = "none";
28422     },
28423     
28424     /**
28425      * Convenience function for boolean show/hide.
28426      * @param {Boolean} visible true to show/false to hide
28427      */
28428     setVisible: function(visible){
28429         if(visible) {
28430             this.show();
28431         }else{
28432             this.hide();
28433         }
28434     },
28435     
28436     /**
28437      * Try to focus this item.
28438      */
28439     focus : function(){
28440         Roo.fly(this.el).focus();
28441     },
28442     
28443     /**
28444      * Disables this item.
28445      */
28446     disable : function(){
28447         Roo.fly(this.td).addClass("x-item-disabled");
28448         this.disabled = true;
28449         this.el.disabled = true;
28450     },
28451     
28452     /**
28453      * Enables this item.
28454      */
28455     enable : function(){
28456         Roo.fly(this.td).removeClass("x-item-disabled");
28457         this.disabled = false;
28458         this.el.disabled = false;
28459     }
28460 };
28461
28462
28463 /**
28464  * @class Roo.Toolbar.Separator
28465  * @extends Roo.Toolbar.Item
28466  * A simple toolbar separator class
28467  * @constructor
28468  * Creates a new Separator
28469  */
28470 Roo.Toolbar.Separator = function(){
28471     var s = document.createElement("span");
28472     s.className = "ytb-sep";
28473     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28474 };
28475 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28476     enable:Roo.emptyFn,
28477     disable:Roo.emptyFn,
28478     focus:Roo.emptyFn
28479 });
28480
28481 /**
28482  * @class Roo.Toolbar.Spacer
28483  * @extends Roo.Toolbar.Item
28484  * A simple element that adds extra horizontal space to a toolbar.
28485  * @constructor
28486  * Creates a new Spacer
28487  */
28488 Roo.Toolbar.Spacer = function(){
28489     var s = document.createElement("div");
28490     s.className = "ytb-spacer";
28491     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28492 };
28493 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28494     enable:Roo.emptyFn,
28495     disable:Roo.emptyFn,
28496     focus:Roo.emptyFn
28497 });
28498
28499 /**
28500  * @class Roo.Toolbar.Fill
28501  * @extends Roo.Toolbar.Spacer
28502  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28503  * @constructor
28504  * Creates a new Spacer
28505  */
28506 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28507     // private
28508     render : function(td){
28509         td.style.width = '100%';
28510         Roo.Toolbar.Fill.superclass.render.call(this, td);
28511     }
28512 });
28513
28514 /**
28515  * @class Roo.Toolbar.TextItem
28516  * @extends Roo.Toolbar.Item
28517  * A simple class that renders text directly into a toolbar.
28518  * @constructor
28519  * Creates a new TextItem
28520  * @param {String} text
28521  */
28522 Roo.Toolbar.TextItem = function(text){
28523     if (typeof(text) == 'object') {
28524         text = text.text;
28525     }
28526     var s = document.createElement("span");
28527     s.className = "ytb-text";
28528     s.innerHTML = text;
28529     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28530 };
28531 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28532     enable:Roo.emptyFn,
28533     disable:Roo.emptyFn,
28534     focus:Roo.emptyFn
28535 });
28536
28537 /**
28538  * @class Roo.Toolbar.Button
28539  * @extends Roo.Button
28540  * A button that renders into a toolbar.
28541  * @constructor
28542  * Creates a new Button
28543  * @param {Object} config A standard {@link Roo.Button} config object
28544  */
28545 Roo.Toolbar.Button = function(config){
28546     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28547 };
28548 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28549     render : function(td){
28550         this.td = td;
28551         Roo.Toolbar.Button.superclass.render.call(this, td);
28552     },
28553     
28554     /**
28555      * Removes and destroys this button
28556      */
28557     destroy : function(){
28558         Roo.Toolbar.Button.superclass.destroy.call(this);
28559         this.td.parentNode.removeChild(this.td);
28560     },
28561     
28562     /**
28563      * Shows this button
28564      */
28565     show: function(){
28566         this.hidden = false;
28567         this.td.style.display = "";
28568     },
28569     
28570     /**
28571      * Hides this button
28572      */
28573     hide: function(){
28574         this.hidden = true;
28575         this.td.style.display = "none";
28576     },
28577
28578     /**
28579      * Disables this item
28580      */
28581     disable : function(){
28582         Roo.fly(this.td).addClass("x-item-disabled");
28583         this.disabled = true;
28584     },
28585
28586     /**
28587      * Enables this item
28588      */
28589     enable : function(){
28590         Roo.fly(this.td).removeClass("x-item-disabled");
28591         this.disabled = false;
28592     }
28593 });
28594 // backwards compat
28595 Roo.ToolbarButton = Roo.Toolbar.Button;
28596
28597 /**
28598  * @class Roo.Toolbar.SplitButton
28599  * @extends Roo.SplitButton
28600  * A menu button that renders into a toolbar.
28601  * @constructor
28602  * Creates a new SplitButton
28603  * @param {Object} config A standard {@link Roo.SplitButton} config object
28604  */
28605 Roo.Toolbar.SplitButton = function(config){
28606     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28607 };
28608 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28609     render : function(td){
28610         this.td = td;
28611         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28612     },
28613     
28614     /**
28615      * Removes and destroys this button
28616      */
28617     destroy : function(){
28618         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28619         this.td.parentNode.removeChild(this.td);
28620     },
28621     
28622     /**
28623      * Shows this button
28624      */
28625     show: function(){
28626         this.hidden = false;
28627         this.td.style.display = "";
28628     },
28629     
28630     /**
28631      * Hides this button
28632      */
28633     hide: function(){
28634         this.hidden = true;
28635         this.td.style.display = "none";
28636     }
28637 });
28638
28639 // backwards compat
28640 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28641  * Based on:
28642  * Ext JS Library 1.1.1
28643  * Copyright(c) 2006-2007, Ext JS, LLC.
28644  *
28645  * Originally Released Under LGPL - original licence link has changed is not relivant.
28646  *
28647  * Fork - LGPL
28648  * <script type="text/javascript">
28649  */
28650  
28651 /**
28652  * @class Roo.PagingToolbar
28653  * @extends Roo.Toolbar
28654  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28655  * @constructor
28656  * Create a new PagingToolbar
28657  * @param {Object} config The config object
28658  */
28659 Roo.PagingToolbar = function(el, ds, config)
28660 {
28661     // old args format still supported... - xtype is prefered..
28662     if (typeof(el) == 'object' && el.xtype) {
28663         // created from xtype...
28664         config = el;
28665         ds = el.dataSource;
28666         el = config.container;
28667     }
28668     var items = [];
28669     if (config.items) {
28670         items = config.items;
28671         config.items = [];
28672     }
28673     
28674     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28675     this.ds = ds;
28676     this.cursor = 0;
28677     this.renderButtons(this.el);
28678     this.bind(ds);
28679     
28680     // supprot items array.
28681    
28682     Roo.each(items, function(e) {
28683         this.add(Roo.factory(e));
28684     },this);
28685     
28686 };
28687
28688 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28689     /**
28690      * @cfg {Roo.data.Store} dataSource
28691      * The underlying data store providing the paged data
28692      */
28693     /**
28694      * @cfg {String/HTMLElement/Element} container
28695      * container The id or element that will contain the toolbar
28696      */
28697     /**
28698      * @cfg {Boolean} displayInfo
28699      * True to display the displayMsg (defaults to false)
28700      */
28701     /**
28702      * @cfg {Number} pageSize
28703      * The number of records to display per page (defaults to 20)
28704      */
28705     pageSize: 20,
28706     /**
28707      * @cfg {String} displayMsg
28708      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28709      */
28710     displayMsg : 'Displaying {0} - {1} of {2}',
28711     /**
28712      * @cfg {String} emptyMsg
28713      * The message to display when no records are found (defaults to "No data to display")
28714      */
28715     emptyMsg : 'No data to display',
28716     /**
28717      * Customizable piece of the default paging text (defaults to "Page")
28718      * @type String
28719      */
28720     beforePageText : "Page",
28721     /**
28722      * Customizable piece of the default paging text (defaults to "of %0")
28723      * @type String
28724      */
28725     afterPageText : "of {0}",
28726     /**
28727      * Customizable piece of the default paging text (defaults to "First Page")
28728      * @type String
28729      */
28730     firstText : "First Page",
28731     /**
28732      * Customizable piece of the default paging text (defaults to "Previous Page")
28733      * @type String
28734      */
28735     prevText : "Previous Page",
28736     /**
28737      * Customizable piece of the default paging text (defaults to "Next Page")
28738      * @type String
28739      */
28740     nextText : "Next Page",
28741     /**
28742      * Customizable piece of the default paging text (defaults to "Last Page")
28743      * @type String
28744      */
28745     lastText : "Last Page",
28746     /**
28747      * Customizable piece of the default paging text (defaults to "Refresh")
28748      * @type String
28749      */
28750     refreshText : "Refresh",
28751
28752     // private
28753     renderButtons : function(el){
28754         Roo.PagingToolbar.superclass.render.call(this, el);
28755         this.first = this.addButton({
28756             tooltip: this.firstText,
28757             cls: "x-btn-icon x-grid-page-first",
28758             disabled: true,
28759             handler: this.onClick.createDelegate(this, ["first"])
28760         });
28761         this.prev = this.addButton({
28762             tooltip: this.prevText,
28763             cls: "x-btn-icon x-grid-page-prev",
28764             disabled: true,
28765             handler: this.onClick.createDelegate(this, ["prev"])
28766         });
28767         //this.addSeparator();
28768         this.add(this.beforePageText);
28769         this.field = Roo.get(this.addDom({
28770            tag: "input",
28771            type: "text",
28772            size: "3",
28773            value: "1",
28774            cls: "x-grid-page-number"
28775         }).el);
28776         this.field.on("keydown", this.onPagingKeydown, this);
28777         this.field.on("focus", function(){this.dom.select();});
28778         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28779         this.field.setHeight(18);
28780         //this.addSeparator();
28781         this.next = this.addButton({
28782             tooltip: this.nextText,
28783             cls: "x-btn-icon x-grid-page-next",
28784             disabled: true,
28785             handler: this.onClick.createDelegate(this, ["next"])
28786         });
28787         this.last = this.addButton({
28788             tooltip: this.lastText,
28789             cls: "x-btn-icon x-grid-page-last",
28790             disabled: true,
28791             handler: this.onClick.createDelegate(this, ["last"])
28792         });
28793         //this.addSeparator();
28794         this.loading = this.addButton({
28795             tooltip: this.refreshText,
28796             cls: "x-btn-icon x-grid-loading",
28797             handler: this.onClick.createDelegate(this, ["refresh"])
28798         });
28799
28800         if(this.displayInfo){
28801             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28802         }
28803     },
28804
28805     // private
28806     updateInfo : function(){
28807         if(this.displayEl){
28808             var count = this.ds.getCount();
28809             var msg = count == 0 ?
28810                 this.emptyMsg :
28811                 String.format(
28812                     this.displayMsg,
28813                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28814                 );
28815             this.displayEl.update(msg);
28816         }
28817     },
28818
28819     // private
28820     onLoad : function(ds, r, o){
28821        this.cursor = o.params ? o.params.start : 0;
28822        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28823
28824        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28825        this.field.dom.value = ap;
28826        this.first.setDisabled(ap == 1);
28827        this.prev.setDisabled(ap == 1);
28828        this.next.setDisabled(ap == ps);
28829        this.last.setDisabled(ap == ps);
28830        this.loading.enable();
28831        this.updateInfo();
28832     },
28833
28834     // private
28835     getPageData : function(){
28836         var total = this.ds.getTotalCount();
28837         return {
28838             total : total,
28839             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28840             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28841         };
28842     },
28843
28844     // private
28845     onLoadError : function(){
28846         this.loading.enable();
28847     },
28848
28849     // private
28850     onPagingKeydown : function(e){
28851         var k = e.getKey();
28852         var d = this.getPageData();
28853         if(k == e.RETURN){
28854             var v = this.field.dom.value, pageNum;
28855             if(!v || isNaN(pageNum = parseInt(v, 10))){
28856                 this.field.dom.value = d.activePage;
28857                 return;
28858             }
28859             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28860             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28861             e.stopEvent();
28862         }
28863         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))
28864         {
28865           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28866           this.field.dom.value = pageNum;
28867           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28868           e.stopEvent();
28869         }
28870         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28871         {
28872           var v = this.field.dom.value, pageNum; 
28873           var increment = (e.shiftKey) ? 10 : 1;
28874           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28875             increment *= -1;
28876           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28877             this.field.dom.value = d.activePage;
28878             return;
28879           }
28880           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28881           {
28882             this.field.dom.value = parseInt(v, 10) + increment;
28883             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28884             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28885           }
28886           e.stopEvent();
28887         }
28888     },
28889
28890     // private
28891     beforeLoad : function(){
28892         if(this.loading){
28893             this.loading.disable();
28894         }
28895     },
28896
28897     // private
28898     onClick : function(which){
28899         var ds = this.ds;
28900         switch(which){
28901             case "first":
28902                 ds.load({params:{start: 0, limit: this.pageSize}});
28903             break;
28904             case "prev":
28905                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28906             break;
28907             case "next":
28908                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28909             break;
28910             case "last":
28911                 var total = ds.getTotalCount();
28912                 var extra = total % this.pageSize;
28913                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28914                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28915             break;
28916             case "refresh":
28917                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28918             break;
28919         }
28920     },
28921
28922     /**
28923      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28924      * @param {Roo.data.Store} store The data store to unbind
28925      */
28926     unbind : function(ds){
28927         ds.un("beforeload", this.beforeLoad, this);
28928         ds.un("load", this.onLoad, this);
28929         ds.un("loadexception", this.onLoadError, this);
28930         ds.un("remove", this.updateInfo, this);
28931         ds.un("add", this.updateInfo, this);
28932         this.ds = undefined;
28933     },
28934
28935     /**
28936      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28937      * @param {Roo.data.Store} store The data store to bind
28938      */
28939     bind : function(ds){
28940         ds.on("beforeload", this.beforeLoad, this);
28941         ds.on("load", this.onLoad, this);
28942         ds.on("loadexception", this.onLoadError, this);
28943         ds.on("remove", this.updateInfo, this);
28944         ds.on("add", this.updateInfo, this);
28945         this.ds = ds;
28946     }
28947 });/*
28948  * Based on:
28949  * Ext JS Library 1.1.1
28950  * Copyright(c) 2006-2007, Ext JS, LLC.
28951  *
28952  * Originally Released Under LGPL - original licence link has changed is not relivant.
28953  *
28954  * Fork - LGPL
28955  * <script type="text/javascript">
28956  */
28957
28958 /**
28959  * @class Roo.Resizable
28960  * @extends Roo.util.Observable
28961  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28962  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28963  * 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
28964  * the element will be wrapped for you automatically.</p>
28965  * <p>Here is the list of valid resize handles:</p>
28966  * <pre>
28967 Value   Description
28968 ------  -------------------
28969  'n'     north
28970  's'     south
28971  'e'     east
28972  'w'     west
28973  'nw'    northwest
28974  'sw'    southwest
28975  'se'    southeast
28976  'ne'    northeast
28977  'hd'    horizontal drag
28978  'all'   all
28979 </pre>
28980  * <p>Here's an example showing the creation of a typical Resizable:</p>
28981  * <pre><code>
28982 var resizer = new Roo.Resizable("element-id", {
28983     handles: 'all',
28984     minWidth: 200,
28985     minHeight: 100,
28986     maxWidth: 500,
28987     maxHeight: 400,
28988     pinned: true
28989 });
28990 resizer.on("resize", myHandler);
28991 </code></pre>
28992  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28993  * resizer.east.setDisplayed(false);</p>
28994  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28995  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28996  * resize operation's new size (defaults to [0, 0])
28997  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28998  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28999  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29000  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29001  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29002  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29003  * @cfg {Number} width The width of the element in pixels (defaults to null)
29004  * @cfg {Number} height The height of the element in pixels (defaults to null)
29005  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29006  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29007  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29008  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29009  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29010  * in favor of the handles config option (defaults to false)
29011  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29012  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29013  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29014  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29015  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29016  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29017  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29018  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29019  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29020  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29021  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29022  * @constructor
29023  * Create a new resizable component
29024  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29025  * @param {Object} config configuration options
29026   */
29027 Roo.Resizable = function(el, config)
29028 {
29029     this.el = Roo.get(el);
29030
29031     if(config && config.wrap){
29032         config.resizeChild = this.el;
29033         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29034         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29035         this.el.setStyle("overflow", "hidden");
29036         this.el.setPositioning(config.resizeChild.getPositioning());
29037         config.resizeChild.clearPositioning();
29038         if(!config.width || !config.height){
29039             var csize = config.resizeChild.getSize();
29040             this.el.setSize(csize.width, csize.height);
29041         }
29042         if(config.pinned && !config.adjustments){
29043             config.adjustments = "auto";
29044         }
29045     }
29046
29047     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29048     this.proxy.unselectable();
29049     this.proxy.enableDisplayMode('block');
29050
29051     Roo.apply(this, config);
29052
29053     if(this.pinned){
29054         this.disableTrackOver = true;
29055         this.el.addClass("x-resizable-pinned");
29056     }
29057     // if the element isn't positioned, make it relative
29058     var position = this.el.getStyle("position");
29059     if(position != "absolute" && position != "fixed"){
29060         this.el.setStyle("position", "relative");
29061     }
29062     if(!this.handles){ // no handles passed, must be legacy style
29063         this.handles = 's,e,se';
29064         if(this.multiDirectional){
29065             this.handles += ',n,w';
29066         }
29067     }
29068     if(this.handles == "all"){
29069         this.handles = "n s e w ne nw se sw";
29070     }
29071     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29072     var ps = Roo.Resizable.positions;
29073     for(var i = 0, len = hs.length; i < len; i++){
29074         if(hs[i] && ps[hs[i]]){
29075             var pos = ps[hs[i]];
29076             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29077         }
29078     }
29079     // legacy
29080     this.corner = this.southeast;
29081     
29082     // updateBox = the box can move..
29083     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29084         this.updateBox = true;
29085     }
29086
29087     this.activeHandle = null;
29088
29089     if(this.resizeChild){
29090         if(typeof this.resizeChild == "boolean"){
29091             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29092         }else{
29093             this.resizeChild = Roo.get(this.resizeChild, true);
29094         }
29095     }
29096     
29097     if(this.adjustments == "auto"){
29098         var rc = this.resizeChild;
29099         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29100         if(rc && (hw || hn)){
29101             rc.position("relative");
29102             rc.setLeft(hw ? hw.el.getWidth() : 0);
29103             rc.setTop(hn ? hn.el.getHeight() : 0);
29104         }
29105         this.adjustments = [
29106             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29107             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29108         ];
29109     }
29110
29111     if(this.draggable){
29112         this.dd = this.dynamic ?
29113             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29114         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29115     }
29116
29117     // public events
29118     this.addEvents({
29119         /**
29120          * @event beforeresize
29121          * Fired before resize is allowed. Set enabled to false to cancel resize.
29122          * @param {Roo.Resizable} this
29123          * @param {Roo.EventObject} e The mousedown event
29124          */
29125         "beforeresize" : true,
29126         /**
29127          * @event resizing
29128          * Fired a resizing.
29129          * @param {Roo.Resizable} this
29130          * @param {Number} x The new x position
29131          * @param {Number} y The new y position
29132          * @param {Number} w The new w width
29133          * @param {Number} h The new h hight
29134          * @param {Roo.EventObject} e The mouseup event
29135          */
29136         "resizing" : true,
29137         /**
29138          * @event resize
29139          * Fired after a resize.
29140          * @param {Roo.Resizable} this
29141          * @param {Number} width The new width
29142          * @param {Number} height The new height
29143          * @param {Roo.EventObject} e The mouseup event
29144          */
29145         "resize" : true
29146     });
29147
29148     if(this.width !== null && this.height !== null){
29149         this.resizeTo(this.width, this.height);
29150     }else{
29151         this.updateChildSize();
29152     }
29153     if(Roo.isIE){
29154         this.el.dom.style.zoom = 1;
29155     }
29156     Roo.Resizable.superclass.constructor.call(this);
29157 };
29158
29159 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29160         resizeChild : false,
29161         adjustments : [0, 0],
29162         minWidth : 5,
29163         minHeight : 5,
29164         maxWidth : 10000,
29165         maxHeight : 10000,
29166         enabled : true,
29167         animate : false,
29168         duration : .35,
29169         dynamic : false,
29170         handles : false,
29171         multiDirectional : false,
29172         disableTrackOver : false,
29173         easing : 'easeOutStrong',
29174         widthIncrement : 0,
29175         heightIncrement : 0,
29176         pinned : false,
29177         width : null,
29178         height : null,
29179         preserveRatio : false,
29180         transparent: false,
29181         minX: 0,
29182         minY: 0,
29183         draggable: false,
29184
29185         /**
29186          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29187          */
29188         constrainTo: undefined,
29189         /**
29190          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29191          */
29192         resizeRegion: undefined,
29193
29194
29195     /**
29196      * Perform a manual resize
29197      * @param {Number} width
29198      * @param {Number} height
29199      */
29200     resizeTo : function(width, height){
29201         this.el.setSize(width, height);
29202         this.updateChildSize();
29203         this.fireEvent("resize", this, width, height, null);
29204     },
29205
29206     // private
29207     startSizing : function(e, handle){
29208         this.fireEvent("beforeresize", this, e);
29209         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29210
29211             if(!this.overlay){
29212                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29213                 this.overlay.unselectable();
29214                 this.overlay.enableDisplayMode("block");
29215                 this.overlay.on("mousemove", this.onMouseMove, this);
29216                 this.overlay.on("mouseup", this.onMouseUp, this);
29217             }
29218             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29219
29220             this.resizing = true;
29221             this.startBox = this.el.getBox();
29222             this.startPoint = e.getXY();
29223             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29224                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29225
29226             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29227             this.overlay.show();
29228
29229             if(this.constrainTo) {
29230                 var ct = Roo.get(this.constrainTo);
29231                 this.resizeRegion = ct.getRegion().adjust(
29232                     ct.getFrameWidth('t'),
29233                     ct.getFrameWidth('l'),
29234                     -ct.getFrameWidth('b'),
29235                     -ct.getFrameWidth('r')
29236                 );
29237             }
29238
29239             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29240             this.proxy.show();
29241             this.proxy.setBox(this.startBox);
29242             if(!this.dynamic){
29243                 this.proxy.setStyle('visibility', 'visible');
29244             }
29245         }
29246     },
29247
29248     // private
29249     onMouseDown : function(handle, e){
29250         if(this.enabled){
29251             e.stopEvent();
29252             this.activeHandle = handle;
29253             this.startSizing(e, handle);
29254         }
29255     },
29256
29257     // private
29258     onMouseUp : function(e){
29259         var size = this.resizeElement();
29260         this.resizing = false;
29261         this.handleOut();
29262         this.overlay.hide();
29263         this.proxy.hide();
29264         this.fireEvent("resize", this, size.width, size.height, e);
29265     },
29266
29267     // private
29268     updateChildSize : function(){
29269         
29270         if(this.resizeChild){
29271             var el = this.el;
29272             var child = this.resizeChild;
29273             var adj = this.adjustments;
29274             if(el.dom.offsetWidth){
29275                 var b = el.getSize(true);
29276                 child.setSize(b.width+adj[0], b.height+adj[1]);
29277             }
29278             // Second call here for IE
29279             // The first call enables instant resizing and
29280             // the second call corrects scroll bars if they
29281             // exist
29282             if(Roo.isIE){
29283                 setTimeout(function(){
29284                     if(el.dom.offsetWidth){
29285                         var b = el.getSize(true);
29286                         child.setSize(b.width+adj[0], b.height+adj[1]);
29287                     }
29288                 }, 10);
29289             }
29290         }
29291     },
29292
29293     // private
29294     snap : function(value, inc, min){
29295         if(!inc || !value) return value;
29296         var newValue = value;
29297         var m = value % inc;
29298         if(m > 0){
29299             if(m > (inc/2)){
29300                 newValue = value + (inc-m);
29301             }else{
29302                 newValue = value - m;
29303             }
29304         }
29305         return Math.max(min, newValue);
29306     },
29307
29308     // private
29309     resizeElement : function(){
29310         var box = this.proxy.getBox();
29311         if(this.updateBox){
29312             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29313         }else{
29314             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29315         }
29316         this.updateChildSize();
29317         if(!this.dynamic){
29318             this.proxy.hide();
29319         }
29320         return box;
29321     },
29322
29323     // private
29324     constrain : function(v, diff, m, mx){
29325         if(v - diff < m){
29326             diff = v - m;
29327         }else if(v - diff > mx){
29328             diff = mx - v;
29329         }
29330         return diff;
29331     },
29332
29333     // private
29334     onMouseMove : function(e){
29335         
29336         if(this.enabled){
29337             try{// try catch so if something goes wrong the user doesn't get hung
29338
29339             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29340                 return;
29341             }
29342
29343             //var curXY = this.startPoint;
29344             var curSize = this.curSize || this.startBox;
29345             var x = this.startBox.x, y = this.startBox.y;
29346             var ox = x, oy = y;
29347             var w = curSize.width, h = curSize.height;
29348             var ow = w, oh = h;
29349             var mw = this.minWidth, mh = this.minHeight;
29350             var mxw = this.maxWidth, mxh = this.maxHeight;
29351             var wi = this.widthIncrement;
29352             var hi = this.heightIncrement;
29353
29354             var eventXY = e.getXY();
29355             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29356             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29357
29358             var pos = this.activeHandle.position;
29359
29360             switch(pos){
29361                 case "east":
29362                     w += diffX;
29363                     w = Math.min(Math.max(mw, w), mxw);
29364                     break;
29365              
29366                 case "south":
29367                     h += diffY;
29368                     h = Math.min(Math.max(mh, h), mxh);
29369                     break;
29370                 case "southeast":
29371                     w += diffX;
29372                     h += diffY;
29373                     w = Math.min(Math.max(mw, w), mxw);
29374                     h = Math.min(Math.max(mh, h), mxh);
29375                     break;
29376                 case "north":
29377                     diffY = this.constrain(h, diffY, mh, mxh);
29378                     y += diffY;
29379                     h -= diffY;
29380                     break;
29381                 case "hdrag":
29382                     
29383                     if (wi) {
29384                         var adiffX = Math.abs(diffX);
29385                         var sub = (adiffX % wi); // how much 
29386                         if (sub > (wi/2)) { // far enough to snap
29387                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29388                         } else {
29389                             // remove difference.. 
29390                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29391                         }
29392                     }
29393                     x += diffX;
29394                     x = Math.max(this.minX, x);
29395                     break;
29396                 case "west":
29397                     diffX = this.constrain(w, diffX, mw, mxw);
29398                     x += diffX;
29399                     w -= diffX;
29400                     break;
29401                 case "northeast":
29402                     w += diffX;
29403                     w = Math.min(Math.max(mw, w), mxw);
29404                     diffY = this.constrain(h, diffY, mh, mxh);
29405                     y += diffY;
29406                     h -= diffY;
29407                     break;
29408                 case "northwest":
29409                     diffX = this.constrain(w, diffX, mw, mxw);
29410                     diffY = this.constrain(h, diffY, mh, mxh);
29411                     y += diffY;
29412                     h -= diffY;
29413                     x += diffX;
29414                     w -= diffX;
29415                     break;
29416                case "southwest":
29417                     diffX = this.constrain(w, diffX, mw, mxw);
29418                     h += diffY;
29419                     h = Math.min(Math.max(mh, h), mxh);
29420                     x += diffX;
29421                     w -= diffX;
29422                     break;
29423             }
29424
29425             var sw = this.snap(w, wi, mw);
29426             var sh = this.snap(h, hi, mh);
29427             if(sw != w || sh != h){
29428                 switch(pos){
29429                     case "northeast":
29430                         y -= sh - h;
29431                     break;
29432                     case "north":
29433                         y -= sh - h;
29434                         break;
29435                     case "southwest":
29436                         x -= sw - w;
29437                     break;
29438                     case "west":
29439                         x -= sw - w;
29440                         break;
29441                     case "northwest":
29442                         x -= sw - w;
29443                         y -= sh - h;
29444                     break;
29445                 }
29446                 w = sw;
29447                 h = sh;
29448             }
29449
29450             if(this.preserveRatio){
29451                 switch(pos){
29452                     case "southeast":
29453                     case "east":
29454                         h = oh * (w/ow);
29455                         h = Math.min(Math.max(mh, h), mxh);
29456                         w = ow * (h/oh);
29457                        break;
29458                     case "south":
29459                         w = ow * (h/oh);
29460                         w = Math.min(Math.max(mw, w), mxw);
29461                         h = oh * (w/ow);
29462                         break;
29463                     case "northeast":
29464                         w = ow * (h/oh);
29465                         w = Math.min(Math.max(mw, w), mxw);
29466                         h = oh * (w/ow);
29467                     break;
29468                     case "north":
29469                         var tw = w;
29470                         w = ow * (h/oh);
29471                         w = Math.min(Math.max(mw, w), mxw);
29472                         h = oh * (w/ow);
29473                         x += (tw - w) / 2;
29474                         break;
29475                     case "southwest":
29476                         h = oh * (w/ow);
29477                         h = Math.min(Math.max(mh, h), mxh);
29478                         var tw = w;
29479                         w = ow * (h/oh);
29480                         x += tw - w;
29481                         break;
29482                     case "west":
29483                         var th = h;
29484                         h = oh * (w/ow);
29485                         h = Math.min(Math.max(mh, h), mxh);
29486                         y += (th - h) / 2;
29487                         var tw = w;
29488                         w = ow * (h/oh);
29489                         x += tw - w;
29490                        break;
29491                     case "northwest":
29492                         var tw = w;
29493                         var th = h;
29494                         h = oh * (w/ow);
29495                         h = Math.min(Math.max(mh, h), mxh);
29496                         w = ow * (h/oh);
29497                         y += th - h;
29498                         x += tw - w;
29499                        break;
29500
29501                 }
29502             }
29503             if (pos == 'hdrag') {
29504                 w = ow;
29505             }
29506             this.proxy.setBounds(x, y, w, h);
29507             if(this.dynamic){
29508                 this.resizeElement();
29509             }
29510             }catch(e){}
29511         }
29512         this.fireEvent("resizing", this, x, y, w, h, e);
29513     },
29514
29515     // private
29516     handleOver : function(){
29517         if(this.enabled){
29518             this.el.addClass("x-resizable-over");
29519         }
29520     },
29521
29522     // private
29523     handleOut : function(){
29524         if(!this.resizing){
29525             this.el.removeClass("x-resizable-over");
29526         }
29527     },
29528
29529     /**
29530      * Returns the element this component is bound to.
29531      * @return {Roo.Element}
29532      */
29533     getEl : function(){
29534         return this.el;
29535     },
29536
29537     /**
29538      * Returns the resizeChild element (or null).
29539      * @return {Roo.Element}
29540      */
29541     getResizeChild : function(){
29542         return this.resizeChild;
29543     },
29544     groupHandler : function()
29545     {
29546         
29547     },
29548     /**
29549      * Destroys this resizable. If the element was wrapped and
29550      * removeEl is not true then the element remains.
29551      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29552      */
29553     destroy : function(removeEl){
29554         this.proxy.remove();
29555         if(this.overlay){
29556             this.overlay.removeAllListeners();
29557             this.overlay.remove();
29558         }
29559         var ps = Roo.Resizable.positions;
29560         for(var k in ps){
29561             if(typeof ps[k] != "function" && this[ps[k]]){
29562                 var h = this[ps[k]];
29563                 h.el.removeAllListeners();
29564                 h.el.remove();
29565             }
29566         }
29567         if(removeEl){
29568             this.el.update("");
29569             this.el.remove();
29570         }
29571     }
29572 });
29573
29574 // private
29575 // hash to map config positions to true positions
29576 Roo.Resizable.positions = {
29577     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29578     hd: "hdrag"
29579 };
29580
29581 // private
29582 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29583     if(!this.tpl){
29584         // only initialize the template if resizable is used
29585         var tpl = Roo.DomHelper.createTemplate(
29586             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29587         );
29588         tpl.compile();
29589         Roo.Resizable.Handle.prototype.tpl = tpl;
29590     }
29591     this.position = pos;
29592     this.rz = rz;
29593     // show north drag fro topdra
29594     var handlepos = pos == 'hdrag' ? 'north' : pos;
29595     
29596     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29597     if (pos == 'hdrag') {
29598         this.el.setStyle('cursor', 'pointer');
29599     }
29600     this.el.unselectable();
29601     if(transparent){
29602         this.el.setOpacity(0);
29603     }
29604     this.el.on("mousedown", this.onMouseDown, this);
29605     if(!disableTrackOver){
29606         this.el.on("mouseover", this.onMouseOver, this);
29607         this.el.on("mouseout", this.onMouseOut, this);
29608     }
29609 };
29610
29611 // private
29612 Roo.Resizable.Handle.prototype = {
29613     afterResize : function(rz){
29614         Roo.log('after?');
29615         // do nothing
29616     },
29617     // private
29618     onMouseDown : function(e){
29619         this.rz.onMouseDown(this, e);
29620     },
29621     // private
29622     onMouseOver : function(e){
29623         this.rz.handleOver(this, e);
29624     },
29625     // private
29626     onMouseOut : function(e){
29627         this.rz.handleOut(this, e);
29628     }
29629 };/*
29630  * Based on:
29631  * Ext JS Library 1.1.1
29632  * Copyright(c) 2006-2007, Ext JS, LLC.
29633  *
29634  * Originally Released Under LGPL - original licence link has changed is not relivant.
29635  *
29636  * Fork - LGPL
29637  * <script type="text/javascript">
29638  */
29639
29640 /**
29641  * @class Roo.Editor
29642  * @extends Roo.Component
29643  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29644  * @constructor
29645  * Create a new Editor
29646  * @param {Roo.form.Field} field The Field object (or descendant)
29647  * @param {Object} config The config object
29648  */
29649 Roo.Editor = function(field, config){
29650     Roo.Editor.superclass.constructor.call(this, config);
29651     this.field = field;
29652     this.addEvents({
29653         /**
29654              * @event beforestartedit
29655              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29656              * false from the handler of this event.
29657              * @param {Editor} this
29658              * @param {Roo.Element} boundEl The underlying element bound to this editor
29659              * @param {Mixed} value The field value being set
29660              */
29661         "beforestartedit" : true,
29662         /**
29663              * @event startedit
29664              * Fires when this editor is displayed
29665              * @param {Roo.Element} boundEl The underlying element bound to this editor
29666              * @param {Mixed} value The starting field value
29667              */
29668         "startedit" : true,
29669         /**
29670              * @event beforecomplete
29671              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29672              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29673              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29674              * event will not fire since no edit actually occurred.
29675              * @param {Editor} this
29676              * @param {Mixed} value The current field value
29677              * @param {Mixed} startValue The original field value
29678              */
29679         "beforecomplete" : true,
29680         /**
29681              * @event complete
29682              * Fires after editing is complete and any changed value has been written to the underlying field.
29683              * @param {Editor} this
29684              * @param {Mixed} value The current field value
29685              * @param {Mixed} startValue The original field value
29686              */
29687         "complete" : true,
29688         /**
29689          * @event specialkey
29690          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29691          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29692          * @param {Roo.form.Field} this
29693          * @param {Roo.EventObject} e The event object
29694          */
29695         "specialkey" : true
29696     });
29697 };
29698
29699 Roo.extend(Roo.Editor, Roo.Component, {
29700     /**
29701      * @cfg {Boolean/String} autosize
29702      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29703      * or "height" to adopt the height only (defaults to false)
29704      */
29705     /**
29706      * @cfg {Boolean} revertInvalid
29707      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29708      * validation fails (defaults to true)
29709      */
29710     /**
29711      * @cfg {Boolean} ignoreNoChange
29712      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29713      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29714      * will never be ignored.
29715      */
29716     /**
29717      * @cfg {Boolean} hideEl
29718      * False to keep the bound element visible while the editor is displayed (defaults to true)
29719      */
29720     /**
29721      * @cfg {Mixed} value
29722      * The data value of the underlying field (defaults to "")
29723      */
29724     value : "",
29725     /**
29726      * @cfg {String} alignment
29727      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29728      */
29729     alignment: "c-c?",
29730     /**
29731      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29732      * for bottom-right shadow (defaults to "frame")
29733      */
29734     shadow : "frame",
29735     /**
29736      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29737      */
29738     constrain : false,
29739     /**
29740      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29741      */
29742     completeOnEnter : false,
29743     /**
29744      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29745      */
29746     cancelOnEsc : false,
29747     /**
29748      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29749      */
29750     updateEl : false,
29751
29752     // private
29753     onRender : function(ct, position){
29754         this.el = new Roo.Layer({
29755             shadow: this.shadow,
29756             cls: "x-editor",
29757             parentEl : ct,
29758             shim : this.shim,
29759             shadowOffset:4,
29760             id: this.id,
29761             constrain: this.constrain
29762         });
29763         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29764         if(this.field.msgTarget != 'title'){
29765             this.field.msgTarget = 'qtip';
29766         }
29767         this.field.render(this.el);
29768         if(Roo.isGecko){
29769             this.field.el.dom.setAttribute('autocomplete', 'off');
29770         }
29771         this.field.on("specialkey", this.onSpecialKey, this);
29772         if(this.swallowKeys){
29773             this.field.el.swallowEvent(['keydown','keypress']);
29774         }
29775         this.field.show();
29776         this.field.on("blur", this.onBlur, this);
29777         if(this.field.grow){
29778             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29779         }
29780     },
29781
29782     onSpecialKey : function(field, e)
29783     {
29784         //Roo.log('editor onSpecialKey');
29785         if(this.completeOnEnter && e.getKey() == e.ENTER){
29786             e.stopEvent();
29787             this.completeEdit();
29788             return;
29789         }
29790         // do not fire special key otherwise it might hide close the editor...
29791         if(e.getKey() == e.ENTER){    
29792             return;
29793         }
29794         if(this.cancelOnEsc && e.getKey() == e.ESC){
29795             this.cancelEdit();
29796             return;
29797         } 
29798         this.fireEvent('specialkey', field, e);
29799     
29800     },
29801
29802     /**
29803      * Starts the editing process and shows the editor.
29804      * @param {String/HTMLElement/Element} el The element to edit
29805      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29806       * to the innerHTML of el.
29807      */
29808     startEdit : function(el, value){
29809         if(this.editing){
29810             this.completeEdit();
29811         }
29812         this.boundEl = Roo.get(el);
29813         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29814         if(!this.rendered){
29815             this.render(this.parentEl || document.body);
29816         }
29817         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29818             return;
29819         }
29820         this.startValue = v;
29821         this.field.setValue(v);
29822         if(this.autoSize){
29823             var sz = this.boundEl.getSize();
29824             switch(this.autoSize){
29825                 case "width":
29826                 this.setSize(sz.width,  "");
29827                 break;
29828                 case "height":
29829                 this.setSize("",  sz.height);
29830                 break;
29831                 default:
29832                 this.setSize(sz.width,  sz.height);
29833             }
29834         }
29835         this.el.alignTo(this.boundEl, this.alignment);
29836         this.editing = true;
29837         if(Roo.QuickTips){
29838             Roo.QuickTips.disable();
29839         }
29840         this.show();
29841     },
29842
29843     /**
29844      * Sets the height and width of this editor.
29845      * @param {Number} width The new width
29846      * @param {Number} height The new height
29847      */
29848     setSize : function(w, h){
29849         this.field.setSize(w, h);
29850         if(this.el){
29851             this.el.sync();
29852         }
29853     },
29854
29855     /**
29856      * Realigns the editor to the bound field based on the current alignment config value.
29857      */
29858     realign : function(){
29859         this.el.alignTo(this.boundEl, this.alignment);
29860     },
29861
29862     /**
29863      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29864      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29865      */
29866     completeEdit : function(remainVisible){
29867         if(!this.editing){
29868             return;
29869         }
29870         var v = this.getValue();
29871         if(this.revertInvalid !== false && !this.field.isValid()){
29872             v = this.startValue;
29873             this.cancelEdit(true);
29874         }
29875         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29876             this.editing = false;
29877             this.hide();
29878             return;
29879         }
29880         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29881             this.editing = false;
29882             if(this.updateEl && this.boundEl){
29883                 this.boundEl.update(v);
29884             }
29885             if(remainVisible !== true){
29886                 this.hide();
29887             }
29888             this.fireEvent("complete", this, v, this.startValue);
29889         }
29890     },
29891
29892     // private
29893     onShow : function(){
29894         this.el.show();
29895         if(this.hideEl !== false){
29896             this.boundEl.hide();
29897         }
29898         this.field.show();
29899         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29900             this.fixIEFocus = true;
29901             this.deferredFocus.defer(50, this);
29902         }else{
29903             this.field.focus();
29904         }
29905         this.fireEvent("startedit", this.boundEl, this.startValue);
29906     },
29907
29908     deferredFocus : function(){
29909         if(this.editing){
29910             this.field.focus();
29911         }
29912     },
29913
29914     /**
29915      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29916      * reverted to the original starting value.
29917      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29918      * cancel (defaults to false)
29919      */
29920     cancelEdit : function(remainVisible){
29921         if(this.editing){
29922             this.setValue(this.startValue);
29923             if(remainVisible !== true){
29924                 this.hide();
29925             }
29926         }
29927     },
29928
29929     // private
29930     onBlur : function(){
29931         if(this.allowBlur !== true && this.editing){
29932             this.completeEdit();
29933         }
29934     },
29935
29936     // private
29937     onHide : function(){
29938         if(this.editing){
29939             this.completeEdit();
29940             return;
29941         }
29942         this.field.blur();
29943         if(this.field.collapse){
29944             this.field.collapse();
29945         }
29946         this.el.hide();
29947         if(this.hideEl !== false){
29948             this.boundEl.show();
29949         }
29950         if(Roo.QuickTips){
29951             Roo.QuickTips.enable();
29952         }
29953     },
29954
29955     /**
29956      * Sets the data value of the editor
29957      * @param {Mixed} value Any valid value supported by the underlying field
29958      */
29959     setValue : function(v){
29960         this.field.setValue(v);
29961     },
29962
29963     /**
29964      * Gets the data value of the editor
29965      * @return {Mixed} The data value
29966      */
29967     getValue : function(){
29968         return this.field.getValue();
29969     }
29970 });/*
29971  * Based on:
29972  * Ext JS Library 1.1.1
29973  * Copyright(c) 2006-2007, Ext JS, LLC.
29974  *
29975  * Originally Released Under LGPL - original licence link has changed is not relivant.
29976  *
29977  * Fork - LGPL
29978  * <script type="text/javascript">
29979  */
29980  
29981 /**
29982  * @class Roo.BasicDialog
29983  * @extends Roo.util.Observable
29984  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29985  * <pre><code>
29986 var dlg = new Roo.BasicDialog("my-dlg", {
29987     height: 200,
29988     width: 300,
29989     minHeight: 100,
29990     minWidth: 150,
29991     modal: true,
29992     proxyDrag: true,
29993     shadow: true
29994 });
29995 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29996 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29997 dlg.addButton('Cancel', dlg.hide, dlg);
29998 dlg.show();
29999 </code></pre>
30000   <b>A Dialog should always be a direct child of the body element.</b>
30001  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30002  * @cfg {String} title Default text to display in the title bar (defaults to null)
30003  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30004  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30005  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30006  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30007  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30008  * (defaults to null with no animation)
30009  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30010  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30011  * property for valid values (defaults to 'all')
30012  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30013  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30014  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30015  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30016  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30017  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30018  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30019  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30020  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30021  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30022  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30023  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30024  * draggable = true (defaults to false)
30025  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30026  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30027  * shadow (defaults to false)
30028  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30029  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30030  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30031  * @cfg {Array} buttons Array of buttons
30032  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30033  * @constructor
30034  * Create a new BasicDialog.
30035  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30036  * @param {Object} config Configuration options
30037  */
30038 Roo.BasicDialog = function(el, config){
30039     this.el = Roo.get(el);
30040     var dh = Roo.DomHelper;
30041     if(!this.el && config && config.autoCreate){
30042         if(typeof config.autoCreate == "object"){
30043             if(!config.autoCreate.id){
30044                 config.autoCreate.id = el;
30045             }
30046             this.el = dh.append(document.body,
30047                         config.autoCreate, true);
30048         }else{
30049             this.el = dh.append(document.body,
30050                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30051         }
30052     }
30053     el = this.el;
30054     el.setDisplayed(true);
30055     el.hide = this.hideAction;
30056     this.id = el.id;
30057     el.addClass("x-dlg");
30058
30059     Roo.apply(this, config);
30060
30061     this.proxy = el.createProxy("x-dlg-proxy");
30062     this.proxy.hide = this.hideAction;
30063     this.proxy.setOpacity(.5);
30064     this.proxy.hide();
30065
30066     if(config.width){
30067         el.setWidth(config.width);
30068     }
30069     if(config.height){
30070         el.setHeight(config.height);
30071     }
30072     this.size = el.getSize();
30073     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30074         this.xy = [config.x,config.y];
30075     }else{
30076         this.xy = el.getCenterXY(true);
30077     }
30078     /** The header element @type Roo.Element */
30079     this.header = el.child("> .x-dlg-hd");
30080     /** The body element @type Roo.Element */
30081     this.body = el.child("> .x-dlg-bd");
30082     /** The footer element @type Roo.Element */
30083     this.footer = el.child("> .x-dlg-ft");
30084
30085     if(!this.header){
30086         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30087     }
30088     if(!this.body){
30089         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30090     }
30091
30092     this.header.unselectable();
30093     if(this.title){
30094         this.header.update(this.title);
30095     }
30096     // this element allows the dialog to be focused for keyboard event
30097     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30098     this.focusEl.swallowEvent("click", true);
30099
30100     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30101
30102     // wrap the body and footer for special rendering
30103     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30104     if(this.footer){
30105         this.bwrap.dom.appendChild(this.footer.dom);
30106     }
30107
30108     this.bg = this.el.createChild({
30109         tag: "div", cls:"x-dlg-bg",
30110         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30111     });
30112     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30113
30114
30115     if(this.autoScroll !== false && !this.autoTabs){
30116         this.body.setStyle("overflow", "auto");
30117     }
30118
30119     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30120
30121     if(this.closable !== false){
30122         this.el.addClass("x-dlg-closable");
30123         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30124         this.close.on("click", this.closeClick, this);
30125         this.close.addClassOnOver("x-dlg-close-over");
30126     }
30127     if(this.collapsible !== false){
30128         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30129         this.collapseBtn.on("click", this.collapseClick, this);
30130         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30131         this.header.on("dblclick", this.collapseClick, this);
30132     }
30133     if(this.resizable !== false){
30134         this.el.addClass("x-dlg-resizable");
30135         this.resizer = new Roo.Resizable(el, {
30136             minWidth: this.minWidth || 80,
30137             minHeight:this.minHeight || 80,
30138             handles: this.resizeHandles || "all",
30139             pinned: true
30140         });
30141         this.resizer.on("beforeresize", this.beforeResize, this);
30142         this.resizer.on("resize", this.onResize, this);
30143     }
30144     if(this.draggable !== false){
30145         el.addClass("x-dlg-draggable");
30146         if (!this.proxyDrag) {
30147             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30148         }
30149         else {
30150             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30151         }
30152         dd.setHandleElId(this.header.id);
30153         dd.endDrag = this.endMove.createDelegate(this);
30154         dd.startDrag = this.startMove.createDelegate(this);
30155         dd.onDrag = this.onDrag.createDelegate(this);
30156         dd.scroll = false;
30157         this.dd = dd;
30158     }
30159     if(this.modal){
30160         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30161         this.mask.enableDisplayMode("block");
30162         this.mask.hide();
30163         this.el.addClass("x-dlg-modal");
30164     }
30165     if(this.shadow){
30166         this.shadow = new Roo.Shadow({
30167             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30168             offset : this.shadowOffset
30169         });
30170     }else{
30171         this.shadowOffset = 0;
30172     }
30173     if(Roo.useShims && this.shim !== false){
30174         this.shim = this.el.createShim();
30175         this.shim.hide = this.hideAction;
30176         this.shim.hide();
30177     }else{
30178         this.shim = false;
30179     }
30180     if(this.autoTabs){
30181         this.initTabs();
30182     }
30183     if (this.buttons) { 
30184         var bts= this.buttons;
30185         this.buttons = [];
30186         Roo.each(bts, function(b) {
30187             this.addButton(b);
30188         }, this);
30189     }
30190     
30191     
30192     this.addEvents({
30193         /**
30194          * @event keydown
30195          * Fires when a key is pressed
30196          * @param {Roo.BasicDialog} this
30197          * @param {Roo.EventObject} e
30198          */
30199         "keydown" : true,
30200         /**
30201          * @event move
30202          * Fires when this dialog is moved by the user.
30203          * @param {Roo.BasicDialog} this
30204          * @param {Number} x The new page X
30205          * @param {Number} y The new page Y
30206          */
30207         "move" : true,
30208         /**
30209          * @event resize
30210          * Fires when this dialog is resized by the user.
30211          * @param {Roo.BasicDialog} this
30212          * @param {Number} width The new width
30213          * @param {Number} height The new height
30214          */
30215         "resize" : true,
30216         /**
30217          * @event beforehide
30218          * Fires before this dialog is hidden.
30219          * @param {Roo.BasicDialog} this
30220          */
30221         "beforehide" : true,
30222         /**
30223          * @event hide
30224          * Fires when this dialog is hidden.
30225          * @param {Roo.BasicDialog} this
30226          */
30227         "hide" : true,
30228         /**
30229          * @event beforeshow
30230          * Fires before this dialog is shown.
30231          * @param {Roo.BasicDialog} this
30232          */
30233         "beforeshow" : true,
30234         /**
30235          * @event show
30236          * Fires when this dialog is shown.
30237          * @param {Roo.BasicDialog} this
30238          */
30239         "show" : true
30240     });
30241     el.on("keydown", this.onKeyDown, this);
30242     el.on("mousedown", this.toFront, this);
30243     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30244     this.el.hide();
30245     Roo.DialogManager.register(this);
30246     Roo.BasicDialog.superclass.constructor.call(this);
30247 };
30248
30249 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30250     shadowOffset: Roo.isIE ? 6 : 5,
30251     minHeight: 80,
30252     minWidth: 200,
30253     minButtonWidth: 75,
30254     defaultButton: null,
30255     buttonAlign: "right",
30256     tabTag: 'div',
30257     firstShow: true,
30258
30259     /**
30260      * Sets the dialog title text
30261      * @param {String} text The title text to display
30262      * @return {Roo.BasicDialog} this
30263      */
30264     setTitle : function(text){
30265         this.header.update(text);
30266         return this;
30267     },
30268
30269     // private
30270     closeClick : function(){
30271         this.hide();
30272     },
30273
30274     // private
30275     collapseClick : function(){
30276         this[this.collapsed ? "expand" : "collapse"]();
30277     },
30278
30279     /**
30280      * Collapses the dialog to its minimized state (only the title bar is visible).
30281      * Equivalent to the user clicking the collapse dialog button.
30282      */
30283     collapse : function(){
30284         if(!this.collapsed){
30285             this.collapsed = true;
30286             this.el.addClass("x-dlg-collapsed");
30287             this.restoreHeight = this.el.getHeight();
30288             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30289         }
30290     },
30291
30292     /**
30293      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30294      * clicking the expand dialog button.
30295      */
30296     expand : function(){
30297         if(this.collapsed){
30298             this.collapsed = false;
30299             this.el.removeClass("x-dlg-collapsed");
30300             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30301         }
30302     },
30303
30304     /**
30305      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30306      * @return {Roo.TabPanel} The tabs component
30307      */
30308     initTabs : function(){
30309         var tabs = this.getTabs();
30310         while(tabs.getTab(0)){
30311             tabs.removeTab(0);
30312         }
30313         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30314             var dom = el.dom;
30315             tabs.addTab(Roo.id(dom), dom.title);
30316             dom.title = "";
30317         });
30318         tabs.activate(0);
30319         return tabs;
30320     },
30321
30322     // private
30323     beforeResize : function(){
30324         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30325     },
30326
30327     // private
30328     onResize : function(){
30329         this.refreshSize();
30330         this.syncBodyHeight();
30331         this.adjustAssets();
30332         this.focus();
30333         this.fireEvent("resize", this, this.size.width, this.size.height);
30334     },
30335
30336     // private
30337     onKeyDown : function(e){
30338         if(this.isVisible()){
30339             this.fireEvent("keydown", this, e);
30340         }
30341     },
30342
30343     /**
30344      * Resizes the dialog.
30345      * @param {Number} width
30346      * @param {Number} height
30347      * @return {Roo.BasicDialog} this
30348      */
30349     resizeTo : function(width, height){
30350         this.el.setSize(width, height);
30351         this.size = {width: width, height: height};
30352         this.syncBodyHeight();
30353         if(this.fixedcenter){
30354             this.center();
30355         }
30356         if(this.isVisible()){
30357             this.constrainXY();
30358             this.adjustAssets();
30359         }
30360         this.fireEvent("resize", this, width, height);
30361         return this;
30362     },
30363
30364
30365     /**
30366      * Resizes the dialog to fit the specified content size.
30367      * @param {Number} width
30368      * @param {Number} height
30369      * @return {Roo.BasicDialog} this
30370      */
30371     setContentSize : function(w, h){
30372         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30373         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30374         //if(!this.el.isBorderBox()){
30375             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30376             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30377         //}
30378         if(this.tabs){
30379             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30380             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30381         }
30382         this.resizeTo(w, h);
30383         return this;
30384     },
30385
30386     /**
30387      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30388      * executed in response to a particular key being pressed while the dialog is active.
30389      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30390      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30391      * @param {Function} fn The function to call
30392      * @param {Object} scope (optional) The scope of the function
30393      * @return {Roo.BasicDialog} this
30394      */
30395     addKeyListener : function(key, fn, scope){
30396         var keyCode, shift, ctrl, alt;
30397         if(typeof key == "object" && !(key instanceof Array)){
30398             keyCode = key["key"];
30399             shift = key["shift"];
30400             ctrl = key["ctrl"];
30401             alt = key["alt"];
30402         }else{
30403             keyCode = key;
30404         }
30405         var handler = function(dlg, e){
30406             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30407                 var k = e.getKey();
30408                 if(keyCode instanceof Array){
30409                     for(var i = 0, len = keyCode.length; i < len; i++){
30410                         if(keyCode[i] == k){
30411                           fn.call(scope || window, dlg, k, e);
30412                           return;
30413                         }
30414                     }
30415                 }else{
30416                     if(k == keyCode){
30417                         fn.call(scope || window, dlg, k, e);
30418                     }
30419                 }
30420             }
30421         };
30422         this.on("keydown", handler);
30423         return this;
30424     },
30425
30426     /**
30427      * Returns the TabPanel component (creates it if it doesn't exist).
30428      * Note: If you wish to simply check for the existence of tabs without creating them,
30429      * check for a null 'tabs' property.
30430      * @return {Roo.TabPanel} The tabs component
30431      */
30432     getTabs : function(){
30433         if(!this.tabs){
30434             this.el.addClass("x-dlg-auto-tabs");
30435             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30436             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30437         }
30438         return this.tabs;
30439     },
30440
30441     /**
30442      * Adds a button to the footer section of the dialog.
30443      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30444      * object or a valid Roo.DomHelper element config
30445      * @param {Function} handler The function called when the button is clicked
30446      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30447      * @return {Roo.Button} The new button
30448      */
30449     addButton : function(config, handler, scope){
30450         var dh = Roo.DomHelper;
30451         if(!this.footer){
30452             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30453         }
30454         if(!this.btnContainer){
30455             var tb = this.footer.createChild({
30456
30457                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30458                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30459             }, null, true);
30460             this.btnContainer = tb.firstChild.firstChild.firstChild;
30461         }
30462         var bconfig = {
30463             handler: handler,
30464             scope: scope,
30465             minWidth: this.minButtonWidth,
30466             hideParent:true
30467         };
30468         if(typeof config == "string"){
30469             bconfig.text = config;
30470         }else{
30471             if(config.tag){
30472                 bconfig.dhconfig = config;
30473             }else{
30474                 Roo.apply(bconfig, config);
30475             }
30476         }
30477         var fc = false;
30478         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30479             bconfig.position = Math.max(0, bconfig.position);
30480             fc = this.btnContainer.childNodes[bconfig.position];
30481         }
30482          
30483         var btn = new Roo.Button(
30484             fc ? 
30485                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30486                 : this.btnContainer.appendChild(document.createElement("td")),
30487             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30488             bconfig
30489         );
30490         this.syncBodyHeight();
30491         if(!this.buttons){
30492             /**
30493              * Array of all the buttons that have been added to this dialog via addButton
30494              * @type Array
30495              */
30496             this.buttons = [];
30497         }
30498         this.buttons.push(btn);
30499         return btn;
30500     },
30501
30502     /**
30503      * Sets the default button to be focused when the dialog is displayed.
30504      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30505      * @return {Roo.BasicDialog} this
30506      */
30507     setDefaultButton : function(btn){
30508         this.defaultButton = btn;
30509         return this;
30510     },
30511
30512     // private
30513     getHeaderFooterHeight : function(safe){
30514         var height = 0;
30515         if(this.header){
30516            height += this.header.getHeight();
30517         }
30518         if(this.footer){
30519            var fm = this.footer.getMargins();
30520             height += (this.footer.getHeight()+fm.top+fm.bottom);
30521         }
30522         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30523         height += this.centerBg.getPadding("tb");
30524         return height;
30525     },
30526
30527     // private
30528     syncBodyHeight : function()
30529     {
30530         var bd = this.body, // the text
30531             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30532             bw = this.bwrap;
30533         var height = this.size.height - this.getHeaderFooterHeight(false);
30534         bd.setHeight(height-bd.getMargins("tb"));
30535         var hh = this.header.getHeight();
30536         var h = this.size.height-hh;
30537         cb.setHeight(h);
30538         
30539         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30540         bw.setHeight(h-cb.getPadding("tb"));
30541         
30542         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30543         bd.setWidth(bw.getWidth(true));
30544         if(this.tabs){
30545             this.tabs.syncHeight();
30546             if(Roo.isIE){
30547                 this.tabs.el.repaint();
30548             }
30549         }
30550     },
30551
30552     /**
30553      * Restores the previous state of the dialog if Roo.state is configured.
30554      * @return {Roo.BasicDialog} this
30555      */
30556     restoreState : function(){
30557         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30558         if(box && box.width){
30559             this.xy = [box.x, box.y];
30560             this.resizeTo(box.width, box.height);
30561         }
30562         return this;
30563     },
30564
30565     // private
30566     beforeShow : function(){
30567         this.expand();
30568         if(this.fixedcenter){
30569             this.xy = this.el.getCenterXY(true);
30570         }
30571         if(this.modal){
30572             Roo.get(document.body).addClass("x-body-masked");
30573             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30574             this.mask.show();
30575         }
30576         this.constrainXY();
30577     },
30578
30579     // private
30580     animShow : function(){
30581         var b = Roo.get(this.animateTarget).getBox();
30582         this.proxy.setSize(b.width, b.height);
30583         this.proxy.setLocation(b.x, b.y);
30584         this.proxy.show();
30585         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30586                     true, .35, this.showEl.createDelegate(this));
30587     },
30588
30589     /**
30590      * Shows the dialog.
30591      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30592      * @return {Roo.BasicDialog} this
30593      */
30594     show : function(animateTarget){
30595         if (this.fireEvent("beforeshow", this) === false){
30596             return;
30597         }
30598         if(this.syncHeightBeforeShow){
30599             this.syncBodyHeight();
30600         }else if(this.firstShow){
30601             this.firstShow = false;
30602             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30603         }
30604         this.animateTarget = animateTarget || this.animateTarget;
30605         if(!this.el.isVisible()){
30606             this.beforeShow();
30607             if(this.animateTarget && Roo.get(this.animateTarget)){
30608                 this.animShow();
30609             }else{
30610                 this.showEl();
30611             }
30612         }
30613         return this;
30614     },
30615
30616     // private
30617     showEl : function(){
30618         this.proxy.hide();
30619         this.el.setXY(this.xy);
30620         this.el.show();
30621         this.adjustAssets(true);
30622         this.toFront();
30623         this.focus();
30624         // IE peekaboo bug - fix found by Dave Fenwick
30625         if(Roo.isIE){
30626             this.el.repaint();
30627         }
30628         this.fireEvent("show", this);
30629     },
30630
30631     /**
30632      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30633      * dialog itself will receive focus.
30634      */
30635     focus : function(){
30636         if(this.defaultButton){
30637             this.defaultButton.focus();
30638         }else{
30639             this.focusEl.focus();
30640         }
30641     },
30642
30643     // private
30644     constrainXY : function(){
30645         if(this.constraintoviewport !== false){
30646             if(!this.viewSize){
30647                 if(this.container){
30648                     var s = this.container.getSize();
30649                     this.viewSize = [s.width, s.height];
30650                 }else{
30651                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30652                 }
30653             }
30654             var s = Roo.get(this.container||document).getScroll();
30655
30656             var x = this.xy[0], y = this.xy[1];
30657             var w = this.size.width, h = this.size.height;
30658             var vw = this.viewSize[0], vh = this.viewSize[1];
30659             // only move it if it needs it
30660             var moved = false;
30661             // first validate right/bottom
30662             if(x + w > vw+s.left){
30663                 x = vw - w;
30664                 moved = true;
30665             }
30666             if(y + h > vh+s.top){
30667                 y = vh - h;
30668                 moved = true;
30669             }
30670             // then make sure top/left isn't negative
30671             if(x < s.left){
30672                 x = s.left;
30673                 moved = true;
30674             }
30675             if(y < s.top){
30676                 y = s.top;
30677                 moved = true;
30678             }
30679             if(moved){
30680                 // cache xy
30681                 this.xy = [x, y];
30682                 if(this.isVisible()){
30683                     this.el.setLocation(x, y);
30684                     this.adjustAssets();
30685                 }
30686             }
30687         }
30688     },
30689
30690     // private
30691     onDrag : function(){
30692         if(!this.proxyDrag){
30693             this.xy = this.el.getXY();
30694             this.adjustAssets();
30695         }
30696     },
30697
30698     // private
30699     adjustAssets : function(doShow){
30700         var x = this.xy[0], y = this.xy[1];
30701         var w = this.size.width, h = this.size.height;
30702         if(doShow === true){
30703             if(this.shadow){
30704                 this.shadow.show(this.el);
30705             }
30706             if(this.shim){
30707                 this.shim.show();
30708             }
30709         }
30710         if(this.shadow && this.shadow.isVisible()){
30711             this.shadow.show(this.el);
30712         }
30713         if(this.shim && this.shim.isVisible()){
30714             this.shim.setBounds(x, y, w, h);
30715         }
30716     },
30717
30718     // private
30719     adjustViewport : function(w, h){
30720         if(!w || !h){
30721             w = Roo.lib.Dom.getViewWidth();
30722             h = Roo.lib.Dom.getViewHeight();
30723         }
30724         // cache the size
30725         this.viewSize = [w, h];
30726         if(this.modal && this.mask.isVisible()){
30727             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30728             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30729         }
30730         if(this.isVisible()){
30731             this.constrainXY();
30732         }
30733     },
30734
30735     /**
30736      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30737      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30738      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30739      */
30740     destroy : function(removeEl){
30741         if(this.isVisible()){
30742             this.animateTarget = null;
30743             this.hide();
30744         }
30745         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30746         if(this.tabs){
30747             this.tabs.destroy(removeEl);
30748         }
30749         Roo.destroy(
30750              this.shim,
30751              this.proxy,
30752              this.resizer,
30753              this.close,
30754              this.mask
30755         );
30756         if(this.dd){
30757             this.dd.unreg();
30758         }
30759         if(this.buttons){
30760            for(var i = 0, len = this.buttons.length; i < len; i++){
30761                this.buttons[i].destroy();
30762            }
30763         }
30764         this.el.removeAllListeners();
30765         if(removeEl === true){
30766             this.el.update("");
30767             this.el.remove();
30768         }
30769         Roo.DialogManager.unregister(this);
30770     },
30771
30772     // private
30773     startMove : function(){
30774         if(this.proxyDrag){
30775             this.proxy.show();
30776         }
30777         if(this.constraintoviewport !== false){
30778             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30779         }
30780     },
30781
30782     // private
30783     endMove : function(){
30784         if(!this.proxyDrag){
30785             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30786         }else{
30787             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30788             this.proxy.hide();
30789         }
30790         this.refreshSize();
30791         this.adjustAssets();
30792         this.focus();
30793         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30794     },
30795
30796     /**
30797      * Brings this dialog to the front of any other visible dialogs
30798      * @return {Roo.BasicDialog} this
30799      */
30800     toFront : function(){
30801         Roo.DialogManager.bringToFront(this);
30802         return this;
30803     },
30804
30805     /**
30806      * Sends this dialog to the back (under) of any other visible dialogs
30807      * @return {Roo.BasicDialog} this
30808      */
30809     toBack : function(){
30810         Roo.DialogManager.sendToBack(this);
30811         return this;
30812     },
30813
30814     /**
30815      * Centers this dialog in the viewport
30816      * @return {Roo.BasicDialog} this
30817      */
30818     center : function(){
30819         var xy = this.el.getCenterXY(true);
30820         this.moveTo(xy[0], xy[1]);
30821         return this;
30822     },
30823
30824     /**
30825      * Moves the dialog's top-left corner to the specified point
30826      * @param {Number} x
30827      * @param {Number} y
30828      * @return {Roo.BasicDialog} this
30829      */
30830     moveTo : function(x, y){
30831         this.xy = [x,y];
30832         if(this.isVisible()){
30833             this.el.setXY(this.xy);
30834             this.adjustAssets();
30835         }
30836         return this;
30837     },
30838
30839     /**
30840      * Aligns the dialog to the specified element
30841      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30842      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30843      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30844      * @return {Roo.BasicDialog} this
30845      */
30846     alignTo : function(element, position, offsets){
30847         this.xy = this.el.getAlignToXY(element, position, offsets);
30848         if(this.isVisible()){
30849             this.el.setXY(this.xy);
30850             this.adjustAssets();
30851         }
30852         return this;
30853     },
30854
30855     /**
30856      * Anchors an element to another element and realigns it when the window is resized.
30857      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30858      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30859      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30860      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30861      * is a number, it is used as the buffer delay (defaults to 50ms).
30862      * @return {Roo.BasicDialog} this
30863      */
30864     anchorTo : function(el, alignment, offsets, monitorScroll){
30865         var action = function(){
30866             this.alignTo(el, alignment, offsets);
30867         };
30868         Roo.EventManager.onWindowResize(action, this);
30869         var tm = typeof monitorScroll;
30870         if(tm != 'undefined'){
30871             Roo.EventManager.on(window, 'scroll', action, this,
30872                 {buffer: tm == 'number' ? monitorScroll : 50});
30873         }
30874         action.call(this);
30875         return this;
30876     },
30877
30878     /**
30879      * Returns true if the dialog is visible
30880      * @return {Boolean}
30881      */
30882     isVisible : function(){
30883         return this.el.isVisible();
30884     },
30885
30886     // private
30887     animHide : function(callback){
30888         var b = Roo.get(this.animateTarget).getBox();
30889         this.proxy.show();
30890         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30891         this.el.hide();
30892         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30893                     this.hideEl.createDelegate(this, [callback]));
30894     },
30895
30896     /**
30897      * Hides the dialog.
30898      * @param {Function} callback (optional) Function to call when the dialog is hidden
30899      * @return {Roo.BasicDialog} this
30900      */
30901     hide : function(callback){
30902         if (this.fireEvent("beforehide", this) === false){
30903             return;
30904         }
30905         if(this.shadow){
30906             this.shadow.hide();
30907         }
30908         if(this.shim) {
30909           this.shim.hide();
30910         }
30911         // sometimes animateTarget seems to get set.. causing problems...
30912         // this just double checks..
30913         if(this.animateTarget && Roo.get(this.animateTarget)) {
30914            this.animHide(callback);
30915         }else{
30916             this.el.hide();
30917             this.hideEl(callback);
30918         }
30919         return this;
30920     },
30921
30922     // private
30923     hideEl : function(callback){
30924         this.proxy.hide();
30925         if(this.modal){
30926             this.mask.hide();
30927             Roo.get(document.body).removeClass("x-body-masked");
30928         }
30929         this.fireEvent("hide", this);
30930         if(typeof callback == "function"){
30931             callback();
30932         }
30933     },
30934
30935     // private
30936     hideAction : function(){
30937         this.setLeft("-10000px");
30938         this.setTop("-10000px");
30939         this.setStyle("visibility", "hidden");
30940     },
30941
30942     // private
30943     refreshSize : function(){
30944         this.size = this.el.getSize();
30945         this.xy = this.el.getXY();
30946         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30947     },
30948
30949     // private
30950     // z-index is managed by the DialogManager and may be overwritten at any time
30951     setZIndex : function(index){
30952         if(this.modal){
30953             this.mask.setStyle("z-index", index);
30954         }
30955         if(this.shim){
30956             this.shim.setStyle("z-index", ++index);
30957         }
30958         if(this.shadow){
30959             this.shadow.setZIndex(++index);
30960         }
30961         this.el.setStyle("z-index", ++index);
30962         if(this.proxy){
30963             this.proxy.setStyle("z-index", ++index);
30964         }
30965         if(this.resizer){
30966             this.resizer.proxy.setStyle("z-index", ++index);
30967         }
30968
30969         this.lastZIndex = index;
30970     },
30971
30972     /**
30973      * Returns the element for this dialog
30974      * @return {Roo.Element} The underlying dialog Element
30975      */
30976     getEl : function(){
30977         return this.el;
30978     }
30979 });
30980
30981 /**
30982  * @class Roo.DialogManager
30983  * Provides global access to BasicDialogs that have been created and
30984  * support for z-indexing (layering) multiple open dialogs.
30985  */
30986 Roo.DialogManager = function(){
30987     var list = {};
30988     var accessList = [];
30989     var front = null;
30990
30991     // private
30992     var sortDialogs = function(d1, d2){
30993         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30994     };
30995
30996     // private
30997     var orderDialogs = function(){
30998         accessList.sort(sortDialogs);
30999         var seed = Roo.DialogManager.zseed;
31000         for(var i = 0, len = accessList.length; i < len; i++){
31001             var dlg = accessList[i];
31002             if(dlg){
31003                 dlg.setZIndex(seed + (i*10));
31004             }
31005         }
31006     };
31007
31008     return {
31009         /**
31010          * The starting z-index for BasicDialogs (defaults to 9000)
31011          * @type Number The z-index value
31012          */
31013         zseed : 9000,
31014
31015         // private
31016         register : function(dlg){
31017             list[dlg.id] = dlg;
31018             accessList.push(dlg);
31019         },
31020
31021         // private
31022         unregister : function(dlg){
31023             delete list[dlg.id];
31024             var i=0;
31025             var len=0;
31026             if(!accessList.indexOf){
31027                 for(  i = 0, len = accessList.length; i < len; i++){
31028                     if(accessList[i] == dlg){
31029                         accessList.splice(i, 1);
31030                         return;
31031                     }
31032                 }
31033             }else{
31034                  i = accessList.indexOf(dlg);
31035                 if(i != -1){
31036                     accessList.splice(i, 1);
31037                 }
31038             }
31039         },
31040
31041         /**
31042          * Gets a registered dialog by id
31043          * @param {String/Object} id The id of the dialog or a dialog
31044          * @return {Roo.BasicDialog} this
31045          */
31046         get : function(id){
31047             return typeof id == "object" ? id : list[id];
31048         },
31049
31050         /**
31051          * Brings the specified dialog to the front
31052          * @param {String/Object} dlg The id of the dialog or a dialog
31053          * @return {Roo.BasicDialog} this
31054          */
31055         bringToFront : function(dlg){
31056             dlg = this.get(dlg);
31057             if(dlg != front){
31058                 front = dlg;
31059                 dlg._lastAccess = new Date().getTime();
31060                 orderDialogs();
31061             }
31062             return dlg;
31063         },
31064
31065         /**
31066          * Sends the specified dialog to the back
31067          * @param {String/Object} dlg The id of the dialog or a dialog
31068          * @return {Roo.BasicDialog} this
31069          */
31070         sendToBack : function(dlg){
31071             dlg = this.get(dlg);
31072             dlg._lastAccess = -(new Date().getTime());
31073             orderDialogs();
31074             return dlg;
31075         },
31076
31077         /**
31078          * Hides all dialogs
31079          */
31080         hideAll : function(){
31081             for(var id in list){
31082                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31083                     list[id].hide();
31084                 }
31085             }
31086         }
31087     };
31088 }();
31089
31090 /**
31091  * @class Roo.LayoutDialog
31092  * @extends Roo.BasicDialog
31093  * Dialog which provides adjustments for working with a layout in a Dialog.
31094  * Add your necessary layout config options to the dialog's config.<br>
31095  * Example usage (including a nested layout):
31096  * <pre><code>
31097 if(!dialog){
31098     dialog = new Roo.LayoutDialog("download-dlg", {
31099         modal: true,
31100         width:600,
31101         height:450,
31102         shadow:true,
31103         minWidth:500,
31104         minHeight:350,
31105         autoTabs:true,
31106         proxyDrag:true,
31107         // layout config merges with the dialog config
31108         center:{
31109             tabPosition: "top",
31110             alwaysShowTabs: true
31111         }
31112     });
31113     dialog.addKeyListener(27, dialog.hide, dialog);
31114     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31115     dialog.addButton("Build It!", this.getDownload, this);
31116
31117     // we can even add nested layouts
31118     var innerLayout = new Roo.BorderLayout("dl-inner", {
31119         east: {
31120             initialSize: 200,
31121             autoScroll:true,
31122             split:true
31123         },
31124         center: {
31125             autoScroll:true
31126         }
31127     });
31128     innerLayout.beginUpdate();
31129     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31130     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31131     innerLayout.endUpdate(true);
31132
31133     var layout = dialog.getLayout();
31134     layout.beginUpdate();
31135     layout.add("center", new Roo.ContentPanel("standard-panel",
31136                         {title: "Download the Source", fitToFrame:true}));
31137     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31138                {title: "Build your own roo.js"}));
31139     layout.getRegion("center").showPanel(sp);
31140     layout.endUpdate();
31141 }
31142 </code></pre>
31143     * @constructor
31144     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31145     * @param {Object} config configuration options
31146   */
31147 Roo.LayoutDialog = function(el, cfg){
31148     
31149     var config=  cfg;
31150     if (typeof(cfg) == 'undefined') {
31151         config = Roo.apply({}, el);
31152         // not sure why we use documentElement here.. - it should always be body.
31153         // IE7 borks horribly if we use documentElement.
31154         // webkit also does not like documentElement - it creates a body element...
31155         el = Roo.get( document.body || document.documentElement ).createChild();
31156         //config.autoCreate = true;
31157     }
31158     
31159     
31160     config.autoTabs = false;
31161     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31162     this.body.setStyle({overflow:"hidden", position:"relative"});
31163     this.layout = new Roo.BorderLayout(this.body.dom, config);
31164     this.layout.monitorWindowResize = false;
31165     this.el.addClass("x-dlg-auto-layout");
31166     // fix case when center region overwrites center function
31167     this.center = Roo.BasicDialog.prototype.center;
31168     this.on("show", this.layout.layout, this.layout, true);
31169     if (config.items) {
31170         var xitems = config.items;
31171         delete config.items;
31172         Roo.each(xitems, this.addxtype, this);
31173     }
31174     
31175     
31176 };
31177 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31178     /**
31179      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31180      * @deprecated
31181      */
31182     endUpdate : function(){
31183         this.layout.endUpdate();
31184     },
31185
31186     /**
31187      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31188      *  @deprecated
31189      */
31190     beginUpdate : function(){
31191         this.layout.beginUpdate();
31192     },
31193
31194     /**
31195      * Get the BorderLayout for this dialog
31196      * @return {Roo.BorderLayout}
31197      */
31198     getLayout : function(){
31199         return this.layout;
31200     },
31201
31202     showEl : function(){
31203         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31204         if(Roo.isIE7){
31205             this.layout.layout();
31206         }
31207     },
31208
31209     // private
31210     // Use the syncHeightBeforeShow config option to control this automatically
31211     syncBodyHeight : function(){
31212         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31213         if(this.layout){this.layout.layout();}
31214     },
31215     
31216       /**
31217      * Add an xtype element (actually adds to the layout.)
31218      * @return {Object} xdata xtype object data.
31219      */
31220     
31221     addxtype : function(c) {
31222         return this.layout.addxtype(c);
31223     }
31224 });/*
31225  * Based on:
31226  * Ext JS Library 1.1.1
31227  * Copyright(c) 2006-2007, Ext JS, LLC.
31228  *
31229  * Originally Released Under LGPL - original licence link has changed is not relivant.
31230  *
31231  * Fork - LGPL
31232  * <script type="text/javascript">
31233  */
31234  
31235 /**
31236  * @class Roo.MessageBox
31237  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31238  * Example usage:
31239  *<pre><code>
31240 // Basic alert:
31241 Roo.Msg.alert('Status', 'Changes saved successfully.');
31242
31243 // Prompt for user data:
31244 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31245     if (btn == 'ok'){
31246         // process text value...
31247     }
31248 });
31249
31250 // Show a dialog using config options:
31251 Roo.Msg.show({
31252    title:'Save Changes?',
31253    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31254    buttons: Roo.Msg.YESNOCANCEL,
31255    fn: processResult,
31256    animEl: 'elId'
31257 });
31258 </code></pre>
31259  * @singleton
31260  */
31261 Roo.MessageBox = function(){
31262     var dlg, opt, mask, waitTimer;
31263     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31264     var buttons, activeTextEl, bwidth;
31265
31266     // private
31267     var handleButton = function(button){
31268         dlg.hide();
31269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31270     };
31271
31272     // private
31273     var handleHide = function(){
31274         if(opt && opt.cls){
31275             dlg.el.removeClass(opt.cls);
31276         }
31277         if(waitTimer){
31278             Roo.TaskMgr.stop(waitTimer);
31279             waitTimer = null;
31280         }
31281     };
31282
31283     // private
31284     var updateButtons = function(b){
31285         var width = 0;
31286         if(!b){
31287             buttons["ok"].hide();
31288             buttons["cancel"].hide();
31289             buttons["yes"].hide();
31290             buttons["no"].hide();
31291             dlg.footer.dom.style.display = 'none';
31292             return width;
31293         }
31294         dlg.footer.dom.style.display = '';
31295         for(var k in buttons){
31296             if(typeof buttons[k] != "function"){
31297                 if(b[k]){
31298                     buttons[k].show();
31299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31300                     width += buttons[k].el.getWidth()+15;
31301                 }else{
31302                     buttons[k].hide();
31303                 }
31304             }
31305         }
31306         return width;
31307     };
31308
31309     // private
31310     var handleEsc = function(d, k, e){
31311         if(opt && opt.closable !== false){
31312             dlg.hide();
31313         }
31314         if(e){
31315             e.stopEvent();
31316         }
31317     };
31318
31319     return {
31320         /**
31321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31322          * @return {Roo.BasicDialog} The BasicDialog element
31323          */
31324         getDialog : function(){
31325            if(!dlg){
31326                 dlg = new Roo.BasicDialog("x-msg-box", {
31327                     autoCreate : true,
31328                     shadow: true,
31329                     draggable: true,
31330                     resizable:false,
31331                     constraintoviewport:false,
31332                     fixedcenter:true,
31333                     collapsible : false,
31334                     shim:true,
31335                     modal: true,
31336                     width:400, height:100,
31337                     buttonAlign:"center",
31338                     closeClick : function(){
31339                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31340                             handleButton("no");
31341                         }else{
31342                             handleButton("cancel");
31343                         }
31344                     }
31345                 });
31346                 dlg.on("hide", handleHide);
31347                 mask = dlg.mask;
31348                 dlg.addKeyListener(27, handleEsc);
31349                 buttons = {};
31350                 var bt = this.buttonText;
31351                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31352                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31353                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31354                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31355                 bodyEl = dlg.body.createChild({
31356
31357                     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>'
31358                 });
31359                 msgEl = bodyEl.dom.firstChild;
31360                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31361                 textboxEl.enableDisplayMode();
31362                 textboxEl.addKeyListener([10,13], function(){
31363                     if(dlg.isVisible() && opt && opt.buttons){
31364                         if(opt.buttons.ok){
31365                             handleButton("ok");
31366                         }else if(opt.buttons.yes){
31367                             handleButton("yes");
31368                         }
31369                     }
31370                 });
31371                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31372                 textareaEl.enableDisplayMode();
31373                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31374                 progressEl.enableDisplayMode();
31375                 var pf = progressEl.dom.firstChild;
31376                 if (pf) {
31377                     pp = Roo.get(pf.firstChild);
31378                     pp.setHeight(pf.offsetHeight);
31379                 }
31380                 
31381             }
31382             return dlg;
31383         },
31384
31385         /**
31386          * Updates the message box body text
31387          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31388          * the XHTML-compliant non-breaking space character '&amp;#160;')
31389          * @return {Roo.MessageBox} This message box
31390          */
31391         updateText : function(text){
31392             if(!dlg.isVisible() && !opt.width){
31393                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31394             }
31395             msgEl.innerHTML = text || '&#160;';
31396       
31397             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31398             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31399             var w = Math.max(
31400                     Math.min(opt.width || cw , this.maxWidth), 
31401                     Math.max(opt.minWidth || this.minWidth, bwidth)
31402             );
31403             if(opt.prompt){
31404                 activeTextEl.setWidth(w);
31405             }
31406             if(dlg.isVisible()){
31407                 dlg.fixedcenter = false;
31408             }
31409             // to big, make it scroll. = But as usual stupid IE does not support
31410             // !important..
31411             
31412             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31413                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31414                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31415             } else {
31416                 bodyEl.dom.style.height = '';
31417                 bodyEl.dom.style.overflowY = '';
31418             }
31419             if (cw > w) {
31420                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31421             } else {
31422                 bodyEl.dom.style.overflowX = '';
31423             }
31424             
31425             dlg.setContentSize(w, bodyEl.getHeight());
31426             if(dlg.isVisible()){
31427                 dlg.fixedcenter = true;
31428             }
31429             return this;
31430         },
31431
31432         /**
31433          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31434          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31435          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31436          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31437          * @return {Roo.MessageBox} This message box
31438          */
31439         updateProgress : function(value, text){
31440             if(text){
31441                 this.updateText(text);
31442             }
31443             if (pp) { // weird bug on my firefox - for some reason this is not defined
31444                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31445             }
31446             return this;
31447         },        
31448
31449         /**
31450          * Returns true if the message box is currently displayed
31451          * @return {Boolean} True if the message box is visible, else false
31452          */
31453         isVisible : function(){
31454             return dlg && dlg.isVisible();  
31455         },
31456
31457         /**
31458          * Hides the message box if it is displayed
31459          */
31460         hide : function(){
31461             if(this.isVisible()){
31462                 dlg.hide();
31463             }  
31464         },
31465
31466         /**
31467          * Displays a new message box, or reinitializes an existing message box, based on the config options
31468          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31469          * The following config object properties are supported:
31470          * <pre>
31471 Property    Type             Description
31472 ----------  ---------------  ------------------------------------------------------------------------------------
31473 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31474                                    closes (defaults to undefined)
31475 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31476                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31477 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31478                                    progress and wait dialogs will ignore this property and always hide the
31479                                    close button as they can only be closed programmatically.
31480 cls               String           A custom CSS class to apply to the message box element
31481 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31482                                    displayed (defaults to 75)
31483 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31484                                    function will be btn (the name of the button that was clicked, if applicable,
31485                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31486                                    Progress and wait dialogs will ignore this option since they do not respond to
31487                                    user actions and can only be closed programmatically, so any required function
31488                                    should be called by the same code after it closes the dialog.
31489 icon              String           A CSS class that provides a background image to be used as an icon for
31490                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31491 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31492 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31493 modal             Boolean          False to allow user interaction with the page while the message box is
31494                                    displayed (defaults to true)
31495 msg               String           A string that will replace the existing message box body text (defaults
31496                                    to the XHTML-compliant non-breaking space character '&#160;')
31497 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31498 progress          Boolean          True to display a progress bar (defaults to false)
31499 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31500 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31501 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31502 title             String           The title text
31503 value             String           The string value to set into the active textbox element if displayed
31504 wait              Boolean          True to display a progress bar (defaults to false)
31505 width             Number           The width of the dialog in pixels
31506 </pre>
31507          *
31508          * Example usage:
31509          * <pre><code>
31510 Roo.Msg.show({
31511    title: 'Address',
31512    msg: 'Please enter your address:',
31513    width: 300,
31514    buttons: Roo.MessageBox.OKCANCEL,
31515    multiline: true,
31516    fn: saveAddress,
31517    animEl: 'addAddressBtn'
31518 });
31519 </code></pre>
31520          * @param {Object} config Configuration options
31521          * @return {Roo.MessageBox} This message box
31522          */
31523         show : function(options)
31524         {
31525             
31526             // this causes nightmares if you show one dialog after another
31527             // especially on callbacks..
31528              
31529             if(this.isVisible()){
31530                 
31531                 this.hide();
31532                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31533                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31534                 Roo.log("New Dialog Message:" +  options.msg )
31535                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31536                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31537                 
31538             }
31539             var d = this.getDialog();
31540             opt = options;
31541             d.setTitle(opt.title || "&#160;");
31542             d.close.setDisplayed(opt.closable !== false);
31543             activeTextEl = textboxEl;
31544             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31545             if(opt.prompt){
31546                 if(opt.multiline){
31547                     textboxEl.hide();
31548                     textareaEl.show();
31549                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31550                         opt.multiline : this.defaultTextHeight);
31551                     activeTextEl = textareaEl;
31552                 }else{
31553                     textboxEl.show();
31554                     textareaEl.hide();
31555                 }
31556             }else{
31557                 textboxEl.hide();
31558                 textareaEl.hide();
31559             }
31560             progressEl.setDisplayed(opt.progress === true);
31561             this.updateProgress(0);
31562             activeTextEl.dom.value = opt.value || "";
31563             if(opt.prompt){
31564                 dlg.setDefaultButton(activeTextEl);
31565             }else{
31566                 var bs = opt.buttons;
31567                 var db = null;
31568                 if(bs && bs.ok){
31569                     db = buttons["ok"];
31570                 }else if(bs && bs.yes){
31571                     db = buttons["yes"];
31572                 }
31573                 dlg.setDefaultButton(db);
31574             }
31575             bwidth = updateButtons(opt.buttons);
31576             this.updateText(opt.msg);
31577             if(opt.cls){
31578                 d.el.addClass(opt.cls);
31579             }
31580             d.proxyDrag = opt.proxyDrag === true;
31581             d.modal = opt.modal !== false;
31582             d.mask = opt.modal !== false ? mask : false;
31583             if(!d.isVisible()){
31584                 // force it to the end of the z-index stack so it gets a cursor in FF
31585                 document.body.appendChild(dlg.el.dom);
31586                 d.animateTarget = null;
31587                 d.show(options.animEl);
31588             }
31589             return this;
31590         },
31591
31592         /**
31593          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31594          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31595          * and closing the message box when the process is complete.
31596          * @param {String} title The title bar text
31597          * @param {String} msg The message box body text
31598          * @return {Roo.MessageBox} This message box
31599          */
31600         progress : function(title, msg){
31601             this.show({
31602                 title : title,
31603                 msg : msg,
31604                 buttons: false,
31605                 progress:true,
31606                 closable:false,
31607                 minWidth: this.minProgressWidth,
31608                 modal : true
31609             });
31610             return this;
31611         },
31612
31613         /**
31614          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31615          * If a callback function is passed it will be called after the user clicks the button, and the
31616          * id of the button that was clicked will be passed as the only parameter to the callback
31617          * (could also be the top-right close button).
31618          * @param {String} title The title bar text
31619          * @param {String} msg The message box body text
31620          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31621          * @param {Object} scope (optional) The scope of the callback function
31622          * @return {Roo.MessageBox} This message box
31623          */
31624         alert : function(title, msg, fn, scope){
31625             this.show({
31626                 title : title,
31627                 msg : msg,
31628                 buttons: this.OK,
31629                 fn: fn,
31630                 scope : scope,
31631                 modal : true
31632             });
31633             return this;
31634         },
31635
31636         /**
31637          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31638          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31639          * You are responsible for closing the message box when the process is complete.
31640          * @param {String} msg The message box body text
31641          * @param {String} title (optional) The title bar text
31642          * @return {Roo.MessageBox} This message box
31643          */
31644         wait : function(msg, title){
31645             this.show({
31646                 title : title,
31647                 msg : msg,
31648                 buttons: false,
31649                 closable:false,
31650                 progress:true,
31651                 modal:true,
31652                 width:300,
31653                 wait:true
31654             });
31655             waitTimer = Roo.TaskMgr.start({
31656                 run: function(i){
31657                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31658                 },
31659                 interval: 1000
31660             });
31661             return this;
31662         },
31663
31664         /**
31665          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31666          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31667          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
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          * @return {Roo.MessageBox} This message box
31673          */
31674         confirm : function(title, msg, fn, scope){
31675             this.show({
31676                 title : title,
31677                 msg : msg,
31678                 buttons: this.YESNO,
31679                 fn: fn,
31680                 scope : scope,
31681                 modal : true
31682             });
31683             return this;
31684         },
31685
31686         /**
31687          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31688          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31689          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31690          * (could also be the top-right close button) and the text that was entered will be passed as the two
31691          * parameters to the callback.
31692          * @param {String} title The title bar text
31693          * @param {String} msg The message box body text
31694          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31695          * @param {Object} scope (optional) The scope of the callback function
31696          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31697          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31698          * @return {Roo.MessageBox} This message box
31699          */
31700         prompt : function(title, msg, fn, scope, multiline){
31701             this.show({
31702                 title : title,
31703                 msg : msg,
31704                 buttons: this.OKCANCEL,
31705                 fn: fn,
31706                 minWidth:250,
31707                 scope : scope,
31708                 prompt:true,
31709                 multiline: multiline,
31710                 modal : true
31711             });
31712             return this;
31713         },
31714
31715         /**
31716          * Button config that displays a single OK button
31717          * @type Object
31718          */
31719         OK : {ok:true},
31720         /**
31721          * Button config that displays Yes and No buttons
31722          * @type Object
31723          */
31724         YESNO : {yes:true, no:true},
31725         /**
31726          * Button config that displays OK and Cancel buttons
31727          * @type Object
31728          */
31729         OKCANCEL : {ok:true, cancel:true},
31730         /**
31731          * Button config that displays Yes, No and Cancel buttons
31732          * @type Object
31733          */
31734         YESNOCANCEL : {yes:true, no:true, cancel:true},
31735
31736         /**
31737          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31738          * @type Number
31739          */
31740         defaultTextHeight : 75,
31741         /**
31742          * The maximum width in pixels of the message box (defaults to 600)
31743          * @type Number
31744          */
31745         maxWidth : 600,
31746         /**
31747          * The minimum width in pixels of the message box (defaults to 100)
31748          * @type Number
31749          */
31750         minWidth : 100,
31751         /**
31752          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31753          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31754          * @type Number
31755          */
31756         minProgressWidth : 250,
31757         /**
31758          * An object containing the default button text strings that can be overriden for localized language support.
31759          * Supported properties are: ok, cancel, yes and no.
31760          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31761          * @type Object
31762          */
31763         buttonText : {
31764             ok : "OK",
31765             cancel : "Cancel",
31766             yes : "Yes",
31767             no : "No"
31768         }
31769     };
31770 }();
31771
31772 /**
31773  * Shorthand for {@link Roo.MessageBox}
31774  */
31775 Roo.Msg = Roo.MessageBox;/*
31776  * Based on:
31777  * Ext JS Library 1.1.1
31778  * Copyright(c) 2006-2007, Ext JS, LLC.
31779  *
31780  * Originally Released Under LGPL - original licence link has changed is not relivant.
31781  *
31782  * Fork - LGPL
31783  * <script type="text/javascript">
31784  */
31785 /**
31786  * @class Roo.QuickTips
31787  * Provides attractive and customizable tooltips for any element.
31788  * @singleton
31789  */
31790 Roo.QuickTips = function(){
31791     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31792     var ce, bd, xy, dd;
31793     var visible = false, disabled = true, inited = false;
31794     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31795     
31796     var onOver = function(e){
31797         if(disabled){
31798             return;
31799         }
31800         var t = e.getTarget();
31801         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31802             return;
31803         }
31804         if(ce && t == ce.el){
31805             clearTimeout(hideProc);
31806             return;
31807         }
31808         if(t && tagEls[t.id]){
31809             tagEls[t.id].el = t;
31810             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31811             return;
31812         }
31813         var ttp, et = Roo.fly(t);
31814         var ns = cfg.namespace;
31815         if(tm.interceptTitles && t.title){
31816             ttp = t.title;
31817             t.qtip = ttp;
31818             t.removeAttribute("title");
31819             e.preventDefault();
31820         }else{
31821             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31822         }
31823         if(ttp){
31824             showProc = show.defer(tm.showDelay, tm, [{
31825                 el: t, 
31826                 text: ttp, 
31827                 width: et.getAttributeNS(ns, cfg.width),
31828                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31829                 title: et.getAttributeNS(ns, cfg.title),
31830                     cls: et.getAttributeNS(ns, cfg.cls)
31831             }]);
31832         }
31833     };
31834     
31835     var onOut = function(e){
31836         clearTimeout(showProc);
31837         var t = e.getTarget();
31838         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31839             hideProc = setTimeout(hide, tm.hideDelay);
31840         }
31841     };
31842     
31843     var onMove = function(e){
31844         if(disabled){
31845             return;
31846         }
31847         xy = e.getXY();
31848         xy[1] += 18;
31849         if(tm.trackMouse && ce){
31850             el.setXY(xy);
31851         }
31852     };
31853     
31854     var onDown = function(e){
31855         clearTimeout(showProc);
31856         clearTimeout(hideProc);
31857         if(!e.within(el)){
31858             if(tm.hideOnClick){
31859                 hide();
31860                 tm.disable();
31861                 tm.enable.defer(100, tm);
31862             }
31863         }
31864     };
31865     
31866     var getPad = function(){
31867         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31868     };
31869
31870     var show = function(o){
31871         if(disabled){
31872             return;
31873         }
31874         clearTimeout(dismissProc);
31875         ce = o;
31876         if(removeCls){ // in case manually hidden
31877             el.removeClass(removeCls);
31878             removeCls = null;
31879         }
31880         if(ce.cls){
31881             el.addClass(ce.cls);
31882             removeCls = ce.cls;
31883         }
31884         if(ce.title){
31885             tipTitle.update(ce.title);
31886             tipTitle.show();
31887         }else{
31888             tipTitle.update('');
31889             tipTitle.hide();
31890         }
31891         el.dom.style.width  = tm.maxWidth+'px';
31892         //tipBody.dom.style.width = '';
31893         tipBodyText.update(o.text);
31894         var p = getPad(), w = ce.width;
31895         if(!w){
31896             var td = tipBodyText.dom;
31897             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31898             if(aw > tm.maxWidth){
31899                 w = tm.maxWidth;
31900             }else if(aw < tm.minWidth){
31901                 w = tm.minWidth;
31902             }else{
31903                 w = aw;
31904             }
31905         }
31906         //tipBody.setWidth(w);
31907         el.setWidth(parseInt(w, 10) + p);
31908         if(ce.autoHide === false){
31909             close.setDisplayed(true);
31910             if(dd){
31911                 dd.unlock();
31912             }
31913         }else{
31914             close.setDisplayed(false);
31915             if(dd){
31916                 dd.lock();
31917             }
31918         }
31919         if(xy){
31920             el.avoidY = xy[1]-18;
31921             el.setXY(xy);
31922         }
31923         if(tm.animate){
31924             el.setOpacity(.1);
31925             el.setStyle("visibility", "visible");
31926             el.fadeIn({callback: afterShow});
31927         }else{
31928             afterShow();
31929         }
31930     };
31931     
31932     var afterShow = function(){
31933         if(ce){
31934             el.show();
31935             esc.enable();
31936             if(tm.autoDismiss && ce.autoHide !== false){
31937                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31938             }
31939         }
31940     };
31941     
31942     var hide = function(noanim){
31943         clearTimeout(dismissProc);
31944         clearTimeout(hideProc);
31945         ce = null;
31946         if(el.isVisible()){
31947             esc.disable();
31948             if(noanim !== true && tm.animate){
31949                 el.fadeOut({callback: afterHide});
31950             }else{
31951                 afterHide();
31952             } 
31953         }
31954     };
31955     
31956     var afterHide = function(){
31957         el.hide();
31958         if(removeCls){
31959             el.removeClass(removeCls);
31960             removeCls = null;
31961         }
31962     };
31963     
31964     return {
31965         /**
31966         * @cfg {Number} minWidth
31967         * The minimum width of the quick tip (defaults to 40)
31968         */
31969        minWidth : 40,
31970         /**
31971         * @cfg {Number} maxWidth
31972         * The maximum width of the quick tip (defaults to 300)
31973         */
31974        maxWidth : 300,
31975         /**
31976         * @cfg {Boolean} interceptTitles
31977         * True to automatically use the element's DOM title value if available (defaults to false)
31978         */
31979        interceptTitles : false,
31980         /**
31981         * @cfg {Boolean} trackMouse
31982         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31983         */
31984        trackMouse : false,
31985         /**
31986         * @cfg {Boolean} hideOnClick
31987         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31988         */
31989        hideOnClick : true,
31990         /**
31991         * @cfg {Number} showDelay
31992         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31993         */
31994        showDelay : 500,
31995         /**
31996         * @cfg {Number} hideDelay
31997         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31998         */
31999        hideDelay : 200,
32000         /**
32001         * @cfg {Boolean} autoHide
32002         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32003         * Used in conjunction with hideDelay.
32004         */
32005        autoHide : true,
32006         /**
32007         * @cfg {Boolean}
32008         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32009         * (defaults to true).  Used in conjunction with autoDismissDelay.
32010         */
32011        autoDismiss : true,
32012         /**
32013         * @cfg {Number}
32014         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32015         */
32016        autoDismissDelay : 5000,
32017        /**
32018         * @cfg {Boolean} animate
32019         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32020         */
32021        animate : false,
32022
32023        /**
32024         * @cfg {String} title
32025         * Title text to display (defaults to '').  This can be any valid HTML markup.
32026         */
32027         title: '',
32028        /**
32029         * @cfg {String} text
32030         * Body text to display (defaults to '').  This can be any valid HTML markup.
32031         */
32032         text : '',
32033        /**
32034         * @cfg {String} cls
32035         * A CSS class to apply to the base quick tip element (defaults to '').
32036         */
32037         cls : '',
32038        /**
32039         * @cfg {Number} width
32040         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32041         * minWidth or maxWidth.
32042         */
32043         width : null,
32044
32045     /**
32046      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32047      * or display QuickTips in a page.
32048      */
32049        init : function(){
32050           tm = Roo.QuickTips;
32051           cfg = tm.tagConfig;
32052           if(!inited){
32053               if(!Roo.isReady){ // allow calling of init() before onReady
32054                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32055                   return;
32056               }
32057               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32058               el.fxDefaults = {stopFx: true};
32059               // maximum custom styling
32060               //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>');
32061               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>');              
32062               tipTitle = el.child('h3');
32063               tipTitle.enableDisplayMode("block");
32064               tipBody = el.child('div.x-tip-bd');
32065               tipBodyText = el.child('div.x-tip-bd-inner');
32066               //bdLeft = el.child('div.x-tip-bd-left');
32067               //bdRight = el.child('div.x-tip-bd-right');
32068               close = el.child('div.x-tip-close');
32069               close.enableDisplayMode("block");
32070               close.on("click", hide);
32071               var d = Roo.get(document);
32072               d.on("mousedown", onDown);
32073               d.on("mouseover", onOver);
32074               d.on("mouseout", onOut);
32075               d.on("mousemove", onMove);
32076               esc = d.addKeyListener(27, hide);
32077               esc.disable();
32078               if(Roo.dd.DD){
32079                   dd = el.initDD("default", null, {
32080                       onDrag : function(){
32081                           el.sync();  
32082                       }
32083                   });
32084                   dd.setHandleElId(tipTitle.id);
32085                   dd.lock();
32086               }
32087               inited = true;
32088           }
32089           this.enable(); 
32090        },
32091
32092     /**
32093      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32094      * are supported:
32095      * <pre>
32096 Property    Type                   Description
32097 ----------  ---------------------  ------------------------------------------------------------------------
32098 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32099      * </ul>
32100      * @param {Object} config The config object
32101      */
32102        register : function(config){
32103            var cs = config instanceof Array ? config : arguments;
32104            for(var i = 0, len = cs.length; i < len; i++) {
32105                var c = cs[i];
32106                var target = c.target;
32107                if(target){
32108                    if(target instanceof Array){
32109                        for(var j = 0, jlen = target.length; j < jlen; j++){
32110                            tagEls[target[j]] = c;
32111                        }
32112                    }else{
32113                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32114                    }
32115                }
32116            }
32117        },
32118
32119     /**
32120      * Removes this quick tip from its element and destroys it.
32121      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32122      */
32123        unregister : function(el){
32124            delete tagEls[Roo.id(el)];
32125        },
32126
32127     /**
32128      * Enable this quick tip.
32129      */
32130        enable : function(){
32131            if(inited && disabled){
32132                locks.pop();
32133                if(locks.length < 1){
32134                    disabled = false;
32135                }
32136            }
32137        },
32138
32139     /**
32140      * Disable this quick tip.
32141      */
32142        disable : function(){
32143           disabled = true;
32144           clearTimeout(showProc);
32145           clearTimeout(hideProc);
32146           clearTimeout(dismissProc);
32147           if(ce){
32148               hide(true);
32149           }
32150           locks.push(1);
32151        },
32152
32153     /**
32154      * Returns true if the quick tip is enabled, else false.
32155      */
32156        isEnabled : function(){
32157             return !disabled;
32158        },
32159
32160         // private
32161        tagConfig : {
32162            namespace : "ext",
32163            attribute : "qtip",
32164            width : "width",
32165            target : "target",
32166            title : "qtitle",
32167            hide : "hide",
32168            cls : "qclass"
32169        }
32170    };
32171 }();
32172
32173 // backwards compat
32174 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32175  * Based on:
32176  * Ext JS Library 1.1.1
32177  * Copyright(c) 2006-2007, Ext JS, LLC.
32178  *
32179  * Originally Released Under LGPL - original licence link has changed is not relivant.
32180  *
32181  * Fork - LGPL
32182  * <script type="text/javascript">
32183  */
32184  
32185
32186 /**
32187  * @class Roo.tree.TreePanel
32188  * @extends Roo.data.Tree
32189
32190  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32191  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32192  * @cfg {Boolean} enableDD true to enable drag and drop
32193  * @cfg {Boolean} enableDrag true to enable just drag
32194  * @cfg {Boolean} enableDrop true to enable just drop
32195  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32196  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32197  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32198  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32199  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32200  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32201  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32202  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32203  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32204  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32205  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32206  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32207  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32208  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32209  * @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>
32210  * @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>
32211  * 
32212  * @constructor
32213  * @param {String/HTMLElement/Element} el The container element
32214  * @param {Object} config
32215  */
32216 Roo.tree.TreePanel = function(el, config){
32217     var root = false;
32218     var loader = false;
32219     if (config.root) {
32220         root = config.root;
32221         delete config.root;
32222     }
32223     if (config.loader) {
32224         loader = config.loader;
32225         delete config.loader;
32226     }
32227     
32228     Roo.apply(this, config);
32229     Roo.tree.TreePanel.superclass.constructor.call(this);
32230     this.el = Roo.get(el);
32231     this.el.addClass('x-tree');
32232     //console.log(root);
32233     if (root) {
32234         this.setRootNode( Roo.factory(root, Roo.tree));
32235     }
32236     if (loader) {
32237         this.loader = Roo.factory(loader, Roo.tree);
32238     }
32239    /**
32240     * Read-only. The id of the container element becomes this TreePanel's id.
32241     */
32242     this.id = this.el.id;
32243     this.addEvents({
32244         /**
32245         * @event beforeload
32246         * Fires before a node is loaded, return false to cancel
32247         * @param {Node} node The node being loaded
32248         */
32249         "beforeload" : true,
32250         /**
32251         * @event load
32252         * Fires when a node is loaded
32253         * @param {Node} node The node that was loaded
32254         */
32255         "load" : true,
32256         /**
32257         * @event textchange
32258         * Fires when the text for a node is changed
32259         * @param {Node} node The node
32260         * @param {String} text The new text
32261         * @param {String} oldText The old text
32262         */
32263         "textchange" : true,
32264         /**
32265         * @event beforeexpand
32266         * Fires before a node is expanded, return false to cancel.
32267         * @param {Node} node The node
32268         * @param {Boolean} deep
32269         * @param {Boolean} anim
32270         */
32271         "beforeexpand" : true,
32272         /**
32273         * @event beforecollapse
32274         * Fires before a node is collapsed, return false to cancel.
32275         * @param {Node} node The node
32276         * @param {Boolean} deep
32277         * @param {Boolean} anim
32278         */
32279         "beforecollapse" : true,
32280         /**
32281         * @event expand
32282         * Fires when a node is expanded
32283         * @param {Node} node The node
32284         */
32285         "expand" : true,
32286         /**
32287         * @event disabledchange
32288         * Fires when the disabled status of a node changes
32289         * @param {Node} node The node
32290         * @param {Boolean} disabled
32291         */
32292         "disabledchange" : true,
32293         /**
32294         * @event collapse
32295         * Fires when a node is collapsed
32296         * @param {Node} node The node
32297         */
32298         "collapse" : true,
32299         /**
32300         * @event beforeclick
32301         * Fires before click processing on a node. Return false to cancel the default action.
32302         * @param {Node} node The node
32303         * @param {Roo.EventObject} e The event object
32304         */
32305         "beforeclick":true,
32306         /**
32307         * @event checkchange
32308         * Fires when a node with a checkbox's checked property changes
32309         * @param {Node} this This node
32310         * @param {Boolean} checked
32311         */
32312         "checkchange":true,
32313         /**
32314         * @event click
32315         * Fires when a node is clicked
32316         * @param {Node} node The node
32317         * @param {Roo.EventObject} e The event object
32318         */
32319         "click":true,
32320         /**
32321         * @event dblclick
32322         * Fires when a node is double clicked
32323         * @param {Node} node The node
32324         * @param {Roo.EventObject} e The event object
32325         */
32326         "dblclick":true,
32327         /**
32328         * @event contextmenu
32329         * Fires when a node is right clicked
32330         * @param {Node} node The node
32331         * @param {Roo.EventObject} e The event object
32332         */
32333         "contextmenu":true,
32334         /**
32335         * @event beforechildrenrendered
32336         * Fires right before the child nodes for a node are rendered
32337         * @param {Node} node The node
32338         */
32339         "beforechildrenrendered":true,
32340         /**
32341         * @event startdrag
32342         * Fires when a node starts being dragged
32343         * @param {Roo.tree.TreePanel} this
32344         * @param {Roo.tree.TreeNode} node
32345         * @param {event} e The raw browser event
32346         */ 
32347        "startdrag" : true,
32348        /**
32349         * @event enddrag
32350         * Fires when a drag operation is complete
32351         * @param {Roo.tree.TreePanel} this
32352         * @param {Roo.tree.TreeNode} node
32353         * @param {event} e The raw browser event
32354         */
32355        "enddrag" : true,
32356        /**
32357         * @event dragdrop
32358         * Fires when a dragged node is dropped on a valid DD target
32359         * @param {Roo.tree.TreePanel} this
32360         * @param {Roo.tree.TreeNode} node
32361         * @param {DD} dd The dd it was dropped on
32362         * @param {event} e The raw browser event
32363         */
32364        "dragdrop" : true,
32365        /**
32366         * @event beforenodedrop
32367         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32368         * passed to handlers has the following properties:<br />
32369         * <ul style="padding:5px;padding-left:16px;">
32370         * <li>tree - The TreePanel</li>
32371         * <li>target - The node being targeted for the drop</li>
32372         * <li>data - The drag data from the drag source</li>
32373         * <li>point - The point of the drop - append, above or below</li>
32374         * <li>source - The drag source</li>
32375         * <li>rawEvent - Raw mouse event</li>
32376         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32377         * to be inserted by setting them on this object.</li>
32378         * <li>cancel - Set this to true to cancel the drop.</li>
32379         * </ul>
32380         * @param {Object} dropEvent
32381         */
32382        "beforenodedrop" : true,
32383        /**
32384         * @event nodedrop
32385         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32386         * passed to handlers has the following properties:<br />
32387         * <ul style="padding:5px;padding-left:16px;">
32388         * <li>tree - The TreePanel</li>
32389         * <li>target - The node being targeted for the drop</li>
32390         * <li>data - The drag data from the drag source</li>
32391         * <li>point - The point of the drop - append, above or below</li>
32392         * <li>source - The drag source</li>
32393         * <li>rawEvent - Raw mouse event</li>
32394         * <li>dropNode - Dropped node(s).</li>
32395         * </ul>
32396         * @param {Object} dropEvent
32397         */
32398        "nodedrop" : true,
32399         /**
32400         * @event nodedragover
32401         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32402         * passed to handlers has the following properties:<br />
32403         * <ul style="padding:5px;padding-left:16px;">
32404         * <li>tree - The TreePanel</li>
32405         * <li>target - The node being targeted for the drop</li>
32406         * <li>data - The drag data from the drag source</li>
32407         * <li>point - The point of the drop - append, above or below</li>
32408         * <li>source - The drag source</li>
32409         * <li>rawEvent - Raw mouse event</li>
32410         * <li>dropNode - Drop node(s) provided by the source.</li>
32411         * <li>cancel - Set this to true to signal drop not allowed.</li>
32412         * </ul>
32413         * @param {Object} dragOverEvent
32414         */
32415        "nodedragover" : true
32416         
32417     });
32418     if(this.singleExpand){
32419        this.on("beforeexpand", this.restrictExpand, this);
32420     }
32421     if (this.editor) {
32422         this.editor.tree = this;
32423         this.editor = Roo.factory(this.editor, Roo.tree);
32424     }
32425     
32426     if (this.selModel) {
32427         this.selModel = Roo.factory(this.selModel, Roo.tree);
32428     }
32429    
32430 };
32431 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32432     rootVisible : true,
32433     animate: Roo.enableFx,
32434     lines : true,
32435     enableDD : false,
32436     hlDrop : Roo.enableFx,
32437   
32438     renderer: false,
32439     
32440     rendererTip: false,
32441     // private
32442     restrictExpand : function(node){
32443         var p = node.parentNode;
32444         if(p){
32445             if(p.expandedChild && p.expandedChild.parentNode == p){
32446                 p.expandedChild.collapse();
32447             }
32448             p.expandedChild = node;
32449         }
32450     },
32451
32452     // private override
32453     setRootNode : function(node){
32454         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32455         if(!this.rootVisible){
32456             node.ui = new Roo.tree.RootTreeNodeUI(node);
32457         }
32458         return node;
32459     },
32460
32461     /**
32462      * Returns the container element for this TreePanel
32463      */
32464     getEl : function(){
32465         return this.el;
32466     },
32467
32468     /**
32469      * Returns the default TreeLoader for this TreePanel
32470      */
32471     getLoader : function(){
32472         return this.loader;
32473     },
32474
32475     /**
32476      * Expand all nodes
32477      */
32478     expandAll : function(){
32479         this.root.expand(true);
32480     },
32481
32482     /**
32483      * Collapse all nodes
32484      */
32485     collapseAll : function(){
32486         this.root.collapse(true);
32487     },
32488
32489     /**
32490      * Returns the selection model used by this TreePanel
32491      */
32492     getSelectionModel : function(){
32493         if(!this.selModel){
32494             this.selModel = new Roo.tree.DefaultSelectionModel();
32495         }
32496         return this.selModel;
32497     },
32498
32499     /**
32500      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32501      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32502      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32503      * @return {Array}
32504      */
32505     getChecked : function(a, startNode){
32506         startNode = startNode || this.root;
32507         var r = [];
32508         var f = function(){
32509             if(this.attributes.checked){
32510                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32511             }
32512         }
32513         startNode.cascade(f);
32514         return r;
32515     },
32516
32517     /**
32518      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32519      * @param {String} path
32520      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32521      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32522      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32523      */
32524     expandPath : function(path, attr, callback){
32525         attr = attr || "id";
32526         var keys = path.split(this.pathSeparator);
32527         var curNode = this.root;
32528         if(curNode.attributes[attr] != keys[1]){ // invalid root
32529             if(callback){
32530                 callback(false, null);
32531             }
32532             return;
32533         }
32534         var index = 1;
32535         var f = function(){
32536             if(++index == keys.length){
32537                 if(callback){
32538                     callback(true, curNode);
32539                 }
32540                 return;
32541             }
32542             var c = curNode.findChild(attr, keys[index]);
32543             if(!c){
32544                 if(callback){
32545                     callback(false, curNode);
32546                 }
32547                 return;
32548             }
32549             curNode = c;
32550             c.expand(false, false, f);
32551         };
32552         curNode.expand(false, false, f);
32553     },
32554
32555     /**
32556      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32557      * @param {String} path
32558      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32559      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32560      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32561      */
32562     selectPath : function(path, attr, callback){
32563         attr = attr || "id";
32564         var keys = path.split(this.pathSeparator);
32565         var v = keys.pop();
32566         if(keys.length > 0){
32567             var f = function(success, node){
32568                 if(success && node){
32569                     var n = node.findChild(attr, v);
32570                     if(n){
32571                         n.select();
32572                         if(callback){
32573                             callback(true, n);
32574                         }
32575                     }else if(callback){
32576                         callback(false, n);
32577                     }
32578                 }else{
32579                     if(callback){
32580                         callback(false, n);
32581                     }
32582                 }
32583             };
32584             this.expandPath(keys.join(this.pathSeparator), attr, f);
32585         }else{
32586             this.root.select();
32587             if(callback){
32588                 callback(true, this.root);
32589             }
32590         }
32591     },
32592
32593     getTreeEl : function(){
32594         return this.el;
32595     },
32596
32597     /**
32598      * Trigger rendering of this TreePanel
32599      */
32600     render : function(){
32601         if (this.innerCt) {
32602             return this; // stop it rendering more than once!!
32603         }
32604         
32605         this.innerCt = this.el.createChild({tag:"ul",
32606                cls:"x-tree-root-ct " +
32607                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32608
32609         if(this.containerScroll){
32610             Roo.dd.ScrollManager.register(this.el);
32611         }
32612         if((this.enableDD || this.enableDrop) && !this.dropZone){
32613            /**
32614             * The dropZone used by this tree if drop is enabled
32615             * @type Roo.tree.TreeDropZone
32616             */
32617              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32618                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32619            });
32620         }
32621         if((this.enableDD || this.enableDrag) && !this.dragZone){
32622            /**
32623             * The dragZone used by this tree if drag is enabled
32624             * @type Roo.tree.TreeDragZone
32625             */
32626             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32627                ddGroup: this.ddGroup || "TreeDD",
32628                scroll: this.ddScroll
32629            });
32630         }
32631         this.getSelectionModel().init(this);
32632         if (!this.root) {
32633             Roo.log("ROOT not set in tree");
32634             return this;
32635         }
32636         this.root.render();
32637         if(!this.rootVisible){
32638             this.root.renderChildren();
32639         }
32640         return this;
32641     }
32642 });/*
32643  * Based on:
32644  * Ext JS Library 1.1.1
32645  * Copyright(c) 2006-2007, Ext JS, LLC.
32646  *
32647  * Originally Released Under LGPL - original licence link has changed is not relivant.
32648  *
32649  * Fork - LGPL
32650  * <script type="text/javascript">
32651  */
32652  
32653
32654 /**
32655  * @class Roo.tree.DefaultSelectionModel
32656  * @extends Roo.util.Observable
32657  * The default single selection for a TreePanel.
32658  * @param {Object} cfg Configuration
32659  */
32660 Roo.tree.DefaultSelectionModel = function(cfg){
32661    this.selNode = null;
32662    
32663    
32664    
32665    this.addEvents({
32666        /**
32667         * @event selectionchange
32668         * Fires when the selected node changes
32669         * @param {DefaultSelectionModel} this
32670         * @param {TreeNode} node the new selection
32671         */
32672        "selectionchange" : true,
32673
32674        /**
32675         * @event beforeselect
32676         * Fires before the selected node changes, return false to cancel the change
32677         * @param {DefaultSelectionModel} this
32678         * @param {TreeNode} node the new selection
32679         * @param {TreeNode} node the old selection
32680         */
32681        "beforeselect" : true
32682    });
32683    
32684     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32685 };
32686
32687 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32688     init : function(tree){
32689         this.tree = tree;
32690         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32691         tree.on("click", this.onNodeClick, this);
32692     },
32693     
32694     onNodeClick : function(node, e){
32695         if (e.ctrlKey && this.selNode == node)  {
32696             this.unselect(node);
32697             return;
32698         }
32699         this.select(node);
32700     },
32701     
32702     /**
32703      * Select a node.
32704      * @param {TreeNode} node The node to select
32705      * @return {TreeNode} The selected node
32706      */
32707     select : function(node){
32708         var last = this.selNode;
32709         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32710             if(last){
32711                 last.ui.onSelectedChange(false);
32712             }
32713             this.selNode = node;
32714             node.ui.onSelectedChange(true);
32715             this.fireEvent("selectionchange", this, node, last);
32716         }
32717         return node;
32718     },
32719     
32720     /**
32721      * Deselect a node.
32722      * @param {TreeNode} node The node to unselect
32723      */
32724     unselect : function(node){
32725         if(this.selNode == node){
32726             this.clearSelections();
32727         }    
32728     },
32729     
32730     /**
32731      * Clear all selections
32732      */
32733     clearSelections : function(){
32734         var n = this.selNode;
32735         if(n){
32736             n.ui.onSelectedChange(false);
32737             this.selNode = null;
32738             this.fireEvent("selectionchange", this, null);
32739         }
32740         return n;
32741     },
32742     
32743     /**
32744      * Get the selected node
32745      * @return {TreeNode} The selected node
32746      */
32747     getSelectedNode : function(){
32748         return this.selNode;    
32749     },
32750     
32751     /**
32752      * Returns true if the node is selected
32753      * @param {TreeNode} node The node to check
32754      * @return {Boolean}
32755      */
32756     isSelected : function(node){
32757         return this.selNode == node;  
32758     },
32759
32760     /**
32761      * Selects the node above the selected node in the tree, intelligently walking the nodes
32762      * @return TreeNode The new selection
32763      */
32764     selectPrevious : function(){
32765         var s = this.selNode || this.lastSelNode;
32766         if(!s){
32767             return null;
32768         }
32769         var ps = s.previousSibling;
32770         if(ps){
32771             if(!ps.isExpanded() || ps.childNodes.length < 1){
32772                 return this.select(ps);
32773             } else{
32774                 var lc = ps.lastChild;
32775                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32776                     lc = lc.lastChild;
32777                 }
32778                 return this.select(lc);
32779             }
32780         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32781             return this.select(s.parentNode);
32782         }
32783         return null;
32784     },
32785
32786     /**
32787      * Selects the node above the selected node in the tree, intelligently walking the nodes
32788      * @return TreeNode The new selection
32789      */
32790     selectNext : function(){
32791         var s = this.selNode || this.lastSelNode;
32792         if(!s){
32793             return null;
32794         }
32795         if(s.firstChild && s.isExpanded()){
32796              return this.select(s.firstChild);
32797          }else if(s.nextSibling){
32798              return this.select(s.nextSibling);
32799          }else if(s.parentNode){
32800             var newS = null;
32801             s.parentNode.bubble(function(){
32802                 if(this.nextSibling){
32803                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32804                     return false;
32805                 }
32806             });
32807             return newS;
32808          }
32809         return null;
32810     },
32811
32812     onKeyDown : function(e){
32813         var s = this.selNode || this.lastSelNode;
32814         // undesirable, but required
32815         var sm = this;
32816         if(!s){
32817             return;
32818         }
32819         var k = e.getKey();
32820         switch(k){
32821              case e.DOWN:
32822                  e.stopEvent();
32823                  this.selectNext();
32824              break;
32825              case e.UP:
32826                  e.stopEvent();
32827                  this.selectPrevious();
32828              break;
32829              case e.RIGHT:
32830                  e.preventDefault();
32831                  if(s.hasChildNodes()){
32832                      if(!s.isExpanded()){
32833                          s.expand();
32834                      }else if(s.firstChild){
32835                          this.select(s.firstChild, e);
32836                      }
32837                  }
32838              break;
32839              case e.LEFT:
32840                  e.preventDefault();
32841                  if(s.hasChildNodes() && s.isExpanded()){
32842                      s.collapse();
32843                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32844                      this.select(s.parentNode, e);
32845                  }
32846              break;
32847         };
32848     }
32849 });
32850
32851 /**
32852  * @class Roo.tree.MultiSelectionModel
32853  * @extends Roo.util.Observable
32854  * Multi selection for a TreePanel.
32855  * @param {Object} cfg Configuration
32856  */
32857 Roo.tree.MultiSelectionModel = function(){
32858    this.selNodes = [];
32859    this.selMap = {};
32860    this.addEvents({
32861        /**
32862         * @event selectionchange
32863         * Fires when the selected nodes change
32864         * @param {MultiSelectionModel} this
32865         * @param {Array} nodes Array of the selected nodes
32866         */
32867        "selectionchange" : true
32868    });
32869    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32870    
32871 };
32872
32873 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32874     init : function(tree){
32875         this.tree = tree;
32876         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32877         tree.on("click", this.onNodeClick, this);
32878     },
32879     
32880     onNodeClick : function(node, e){
32881         this.select(node, e, e.ctrlKey);
32882     },
32883     
32884     /**
32885      * Select a node.
32886      * @param {TreeNode} node The node to select
32887      * @param {EventObject} e (optional) An event associated with the selection
32888      * @param {Boolean} keepExisting True to retain existing selections
32889      * @return {TreeNode} The selected node
32890      */
32891     select : function(node, e, keepExisting){
32892         if(keepExisting !== true){
32893             this.clearSelections(true);
32894         }
32895         if(this.isSelected(node)){
32896             this.lastSelNode = node;
32897             return node;
32898         }
32899         this.selNodes.push(node);
32900         this.selMap[node.id] = node;
32901         this.lastSelNode = node;
32902         node.ui.onSelectedChange(true);
32903         this.fireEvent("selectionchange", this, this.selNodes);
32904         return node;
32905     },
32906     
32907     /**
32908      * Deselect a node.
32909      * @param {TreeNode} node The node to unselect
32910      */
32911     unselect : function(node){
32912         if(this.selMap[node.id]){
32913             node.ui.onSelectedChange(false);
32914             var sn = this.selNodes;
32915             var index = -1;
32916             if(sn.indexOf){
32917                 index = sn.indexOf(node);
32918             }else{
32919                 for(var i = 0, len = sn.length; i < len; i++){
32920                     if(sn[i] == node){
32921                         index = i;
32922                         break;
32923                     }
32924                 }
32925             }
32926             if(index != -1){
32927                 this.selNodes.splice(index, 1);
32928             }
32929             delete this.selMap[node.id];
32930             this.fireEvent("selectionchange", this, this.selNodes);
32931         }
32932     },
32933     
32934     /**
32935      * Clear all selections
32936      */
32937     clearSelections : function(suppressEvent){
32938         var sn = this.selNodes;
32939         if(sn.length > 0){
32940             for(var i = 0, len = sn.length; i < len; i++){
32941                 sn[i].ui.onSelectedChange(false);
32942             }
32943             this.selNodes = [];
32944             this.selMap = {};
32945             if(suppressEvent !== true){
32946                 this.fireEvent("selectionchange", this, this.selNodes);
32947             }
32948         }
32949     },
32950     
32951     /**
32952      * Returns true if the node is selected
32953      * @param {TreeNode} node The node to check
32954      * @return {Boolean}
32955      */
32956     isSelected : function(node){
32957         return this.selMap[node.id] ? true : false;  
32958     },
32959     
32960     /**
32961      * Returns an array of the selected nodes
32962      * @return {Array}
32963      */
32964     getSelectedNodes : function(){
32965         return this.selNodes;    
32966     },
32967
32968     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32969
32970     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32971
32972     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32973 });/*
32974  * Based on:
32975  * Ext JS Library 1.1.1
32976  * Copyright(c) 2006-2007, Ext JS, LLC.
32977  *
32978  * Originally Released Under LGPL - original licence link has changed is not relivant.
32979  *
32980  * Fork - LGPL
32981  * <script type="text/javascript">
32982  */
32983  
32984 /**
32985  * @class Roo.tree.TreeNode
32986  * @extends Roo.data.Node
32987  * @cfg {String} text The text for this node
32988  * @cfg {Boolean} expanded true to start the node expanded
32989  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32990  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32991  * @cfg {Boolean} disabled true to start the node disabled
32992  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32993  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32994  * @cfg {String} cls A css class to be added to the node
32995  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32996  * @cfg {String} href URL of the link used for the node (defaults to #)
32997  * @cfg {String} hrefTarget target frame for the link
32998  * @cfg {String} qtip An Ext QuickTip for the node
32999  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33000  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33001  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33002  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33003  * (defaults to undefined with no checkbox rendered)
33004  * @constructor
33005  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33006  */
33007 Roo.tree.TreeNode = function(attributes){
33008     attributes = attributes || {};
33009     if(typeof attributes == "string"){
33010         attributes = {text: attributes};
33011     }
33012     this.childrenRendered = false;
33013     this.rendered = false;
33014     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33015     this.expanded = attributes.expanded === true;
33016     this.isTarget = attributes.isTarget !== false;
33017     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33018     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33019
33020     /**
33021      * Read-only. The text for this node. To change it use setText().
33022      * @type String
33023      */
33024     this.text = attributes.text;
33025     /**
33026      * True if this node is disabled.
33027      * @type Boolean
33028      */
33029     this.disabled = attributes.disabled === true;
33030
33031     this.addEvents({
33032         /**
33033         * @event textchange
33034         * Fires when the text for this node is changed
33035         * @param {Node} this This node
33036         * @param {String} text The new text
33037         * @param {String} oldText The old text
33038         */
33039         "textchange" : true,
33040         /**
33041         * @event beforeexpand
33042         * Fires before this node is expanded, return false to cancel.
33043         * @param {Node} this This node
33044         * @param {Boolean} deep
33045         * @param {Boolean} anim
33046         */
33047         "beforeexpand" : true,
33048         /**
33049         * @event beforecollapse
33050         * Fires before this node is collapsed, return false to cancel.
33051         * @param {Node} this This node
33052         * @param {Boolean} deep
33053         * @param {Boolean} anim
33054         */
33055         "beforecollapse" : true,
33056         /**
33057         * @event expand
33058         * Fires when this node is expanded
33059         * @param {Node} this This node
33060         */
33061         "expand" : true,
33062         /**
33063         * @event disabledchange
33064         * Fires when the disabled status of this node changes
33065         * @param {Node} this This node
33066         * @param {Boolean} disabled
33067         */
33068         "disabledchange" : true,
33069         /**
33070         * @event collapse
33071         * Fires when this node is collapsed
33072         * @param {Node} this This node
33073         */
33074         "collapse" : true,
33075         /**
33076         * @event beforeclick
33077         * Fires before click processing. Return false to cancel the default action.
33078         * @param {Node} this This node
33079         * @param {Roo.EventObject} e The event object
33080         */
33081         "beforeclick":true,
33082         /**
33083         * @event checkchange
33084         * Fires when a node with a checkbox's checked property changes
33085         * @param {Node} this This node
33086         * @param {Boolean} checked
33087         */
33088         "checkchange":true,
33089         /**
33090         * @event click
33091         * Fires when this node is clicked
33092         * @param {Node} this This node
33093         * @param {Roo.EventObject} e The event object
33094         */
33095         "click":true,
33096         /**
33097         * @event dblclick
33098         * Fires when this node is double clicked
33099         * @param {Node} this This node
33100         * @param {Roo.EventObject} e The event object
33101         */
33102         "dblclick":true,
33103         /**
33104         * @event contextmenu
33105         * Fires when this node is right clicked
33106         * @param {Node} this This node
33107         * @param {Roo.EventObject} e The event object
33108         */
33109         "contextmenu":true,
33110         /**
33111         * @event beforechildrenrendered
33112         * Fires right before the child nodes for this node are rendered
33113         * @param {Node} this This node
33114         */
33115         "beforechildrenrendered":true
33116     });
33117
33118     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33119
33120     /**
33121      * Read-only. The UI for this node
33122      * @type TreeNodeUI
33123      */
33124     this.ui = new uiClass(this);
33125     
33126     // finally support items[]
33127     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33128         return;
33129     }
33130     
33131     
33132     Roo.each(this.attributes.items, function(c) {
33133         this.appendChild(Roo.factory(c,Roo.Tree));
33134     }, this);
33135     delete this.attributes.items;
33136     
33137     
33138     
33139 };
33140 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33141     preventHScroll: true,
33142     /**
33143      * Returns true if this node is expanded
33144      * @return {Boolean}
33145      */
33146     isExpanded : function(){
33147         return this.expanded;
33148     },
33149
33150     /**
33151      * Returns the UI object for this node
33152      * @return {TreeNodeUI}
33153      */
33154     getUI : function(){
33155         return this.ui;
33156     },
33157
33158     // private override
33159     setFirstChild : function(node){
33160         var of = this.firstChild;
33161         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33162         if(this.childrenRendered && of && node != of){
33163             of.renderIndent(true, true);
33164         }
33165         if(this.rendered){
33166             this.renderIndent(true, true);
33167         }
33168     },
33169
33170     // private override
33171     setLastChild : function(node){
33172         var ol = this.lastChild;
33173         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33174         if(this.childrenRendered && ol && node != ol){
33175             ol.renderIndent(true, true);
33176         }
33177         if(this.rendered){
33178             this.renderIndent(true, true);
33179         }
33180     },
33181
33182     // these methods are overridden to provide lazy rendering support
33183     // private override
33184     appendChild : function()
33185     {
33186         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33187         if(node && this.childrenRendered){
33188             node.render();
33189         }
33190         this.ui.updateExpandIcon();
33191         return node;
33192     },
33193
33194     // private override
33195     removeChild : function(node){
33196         this.ownerTree.getSelectionModel().unselect(node);
33197         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33198         // if it's been rendered remove dom node
33199         if(this.childrenRendered){
33200             node.ui.remove();
33201         }
33202         if(this.childNodes.length < 1){
33203             this.collapse(false, false);
33204         }else{
33205             this.ui.updateExpandIcon();
33206         }
33207         if(!this.firstChild) {
33208             this.childrenRendered = false;
33209         }
33210         return node;
33211     },
33212
33213     // private override
33214     insertBefore : function(node, refNode){
33215         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33216         if(newNode && refNode && this.childrenRendered){
33217             node.render();
33218         }
33219         this.ui.updateExpandIcon();
33220         return newNode;
33221     },
33222
33223     /**
33224      * Sets the text for this node
33225      * @param {String} text
33226      */
33227     setText : function(text){
33228         var oldText = this.text;
33229         this.text = text;
33230         this.attributes.text = text;
33231         if(this.rendered){ // event without subscribing
33232             this.ui.onTextChange(this, text, oldText);
33233         }
33234         this.fireEvent("textchange", this, text, oldText);
33235     },
33236
33237     /**
33238      * Triggers selection of this node
33239      */
33240     select : function(){
33241         this.getOwnerTree().getSelectionModel().select(this);
33242     },
33243
33244     /**
33245      * Triggers deselection of this node
33246      */
33247     unselect : function(){
33248         this.getOwnerTree().getSelectionModel().unselect(this);
33249     },
33250
33251     /**
33252      * Returns true if this node is selected
33253      * @return {Boolean}
33254      */
33255     isSelected : function(){
33256         return this.getOwnerTree().getSelectionModel().isSelected(this);
33257     },
33258
33259     /**
33260      * Expand this node.
33261      * @param {Boolean} deep (optional) True to expand all children as well
33262      * @param {Boolean} anim (optional) false to cancel the default animation
33263      * @param {Function} callback (optional) A callback to be called when
33264      * expanding this node completes (does not wait for deep expand to complete).
33265      * Called with 1 parameter, this node.
33266      */
33267     expand : function(deep, anim, callback){
33268         if(!this.expanded){
33269             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33270                 return;
33271             }
33272             if(!this.childrenRendered){
33273                 this.renderChildren();
33274             }
33275             this.expanded = true;
33276             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33277                 this.ui.animExpand(function(){
33278                     this.fireEvent("expand", this);
33279                     if(typeof callback == "function"){
33280                         callback(this);
33281                     }
33282                     if(deep === true){
33283                         this.expandChildNodes(true);
33284                     }
33285                 }.createDelegate(this));
33286                 return;
33287             }else{
33288                 this.ui.expand();
33289                 this.fireEvent("expand", this);
33290                 if(typeof callback == "function"){
33291                     callback(this);
33292                 }
33293             }
33294         }else{
33295            if(typeof callback == "function"){
33296                callback(this);
33297            }
33298         }
33299         if(deep === true){
33300             this.expandChildNodes(true);
33301         }
33302     },
33303
33304     isHiddenRoot : function(){
33305         return this.isRoot && !this.getOwnerTree().rootVisible;
33306     },
33307
33308     /**
33309      * Collapse this node.
33310      * @param {Boolean} deep (optional) True to collapse all children as well
33311      * @param {Boolean} anim (optional) false to cancel the default animation
33312      */
33313     collapse : function(deep, anim){
33314         if(this.expanded && !this.isHiddenRoot()){
33315             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33316                 return;
33317             }
33318             this.expanded = false;
33319             if((this.getOwnerTree().animate && anim !== false) || anim){
33320                 this.ui.animCollapse(function(){
33321                     this.fireEvent("collapse", this);
33322                     if(deep === true){
33323                         this.collapseChildNodes(true);
33324                     }
33325                 }.createDelegate(this));
33326                 return;
33327             }else{
33328                 this.ui.collapse();
33329                 this.fireEvent("collapse", this);
33330             }
33331         }
33332         if(deep === true){
33333             var cs = this.childNodes;
33334             for(var i = 0, len = cs.length; i < len; i++) {
33335                 cs[i].collapse(true, false);
33336             }
33337         }
33338     },
33339
33340     // private
33341     delayedExpand : function(delay){
33342         if(!this.expandProcId){
33343             this.expandProcId = this.expand.defer(delay, this);
33344         }
33345     },
33346
33347     // private
33348     cancelExpand : function(){
33349         if(this.expandProcId){
33350             clearTimeout(this.expandProcId);
33351         }
33352         this.expandProcId = false;
33353     },
33354
33355     /**
33356      * Toggles expanded/collapsed state of the node
33357      */
33358     toggle : function(){
33359         if(this.expanded){
33360             this.collapse();
33361         }else{
33362             this.expand();
33363         }
33364     },
33365
33366     /**
33367      * Ensures all parent nodes are expanded
33368      */
33369     ensureVisible : function(callback){
33370         var tree = this.getOwnerTree();
33371         tree.expandPath(this.parentNode.getPath(), false, function(){
33372             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33373             Roo.callback(callback);
33374         }.createDelegate(this));
33375     },
33376
33377     /**
33378      * Expand all child nodes
33379      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33380      */
33381     expandChildNodes : function(deep){
33382         var cs = this.childNodes;
33383         for(var i = 0, len = cs.length; i < len; i++) {
33384                 cs[i].expand(deep);
33385         }
33386     },
33387
33388     /**
33389      * Collapse all child nodes
33390      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33391      */
33392     collapseChildNodes : function(deep){
33393         var cs = this.childNodes;
33394         for(var i = 0, len = cs.length; i < len; i++) {
33395                 cs[i].collapse(deep);
33396         }
33397     },
33398
33399     /**
33400      * Disables this node
33401      */
33402     disable : function(){
33403         this.disabled = true;
33404         this.unselect();
33405         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33406             this.ui.onDisableChange(this, true);
33407         }
33408         this.fireEvent("disabledchange", this, true);
33409     },
33410
33411     /**
33412      * Enables this node
33413      */
33414     enable : function(){
33415         this.disabled = false;
33416         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33417             this.ui.onDisableChange(this, false);
33418         }
33419         this.fireEvent("disabledchange", this, false);
33420     },
33421
33422     // private
33423     renderChildren : function(suppressEvent){
33424         if(suppressEvent !== false){
33425             this.fireEvent("beforechildrenrendered", this);
33426         }
33427         var cs = this.childNodes;
33428         for(var i = 0, len = cs.length; i < len; i++){
33429             cs[i].render(true);
33430         }
33431         this.childrenRendered = true;
33432     },
33433
33434     // private
33435     sort : function(fn, scope){
33436         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33437         if(this.childrenRendered){
33438             var cs = this.childNodes;
33439             for(var i = 0, len = cs.length; i < len; i++){
33440                 cs[i].render(true);
33441             }
33442         }
33443     },
33444
33445     // private
33446     render : function(bulkRender){
33447         this.ui.render(bulkRender);
33448         if(!this.rendered){
33449             this.rendered = true;
33450             if(this.expanded){
33451                 this.expanded = false;
33452                 this.expand(false, false);
33453             }
33454         }
33455     },
33456
33457     // private
33458     renderIndent : function(deep, refresh){
33459         if(refresh){
33460             this.ui.childIndent = null;
33461         }
33462         this.ui.renderIndent();
33463         if(deep === true && this.childrenRendered){
33464             var cs = this.childNodes;
33465             for(var i = 0, len = cs.length; i < len; i++){
33466                 cs[i].renderIndent(true, refresh);
33467             }
33468         }
33469     }
33470 });/*
33471  * Based on:
33472  * Ext JS Library 1.1.1
33473  * Copyright(c) 2006-2007, Ext JS, LLC.
33474  *
33475  * Originally Released Under LGPL - original licence link has changed is not relivant.
33476  *
33477  * Fork - LGPL
33478  * <script type="text/javascript">
33479  */
33480  
33481 /**
33482  * @class Roo.tree.AsyncTreeNode
33483  * @extends Roo.tree.TreeNode
33484  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33485  * @constructor
33486  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33487  */
33488  Roo.tree.AsyncTreeNode = function(config){
33489     this.loaded = false;
33490     this.loading = false;
33491     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33492     /**
33493     * @event beforeload
33494     * Fires before this node is loaded, return false to cancel
33495     * @param {Node} this This node
33496     */
33497     this.addEvents({'beforeload':true, 'load': true});
33498     /**
33499     * @event load
33500     * Fires when this node is loaded
33501     * @param {Node} this This node
33502     */
33503     /**
33504      * The loader used by this node (defaults to using the tree's defined loader)
33505      * @type TreeLoader
33506      * @property loader
33507      */
33508 };
33509 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33510     expand : function(deep, anim, callback){
33511         if(this.loading){ // if an async load is already running, waiting til it's done
33512             var timer;
33513             var f = function(){
33514                 if(!this.loading){ // done loading
33515                     clearInterval(timer);
33516                     this.expand(deep, anim, callback);
33517                 }
33518             }.createDelegate(this);
33519             timer = setInterval(f, 200);
33520             return;
33521         }
33522         if(!this.loaded){
33523             if(this.fireEvent("beforeload", this) === false){
33524                 return;
33525             }
33526             this.loading = true;
33527             this.ui.beforeLoad(this);
33528             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33529             if(loader){
33530                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33531                 return;
33532             }
33533         }
33534         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33535     },
33536     
33537     /**
33538      * Returns true if this node is currently loading
33539      * @return {Boolean}
33540      */
33541     isLoading : function(){
33542         return this.loading;  
33543     },
33544     
33545     loadComplete : function(deep, anim, callback){
33546         this.loading = false;
33547         this.loaded = true;
33548         this.ui.afterLoad(this);
33549         this.fireEvent("load", this);
33550         this.expand(deep, anim, callback);
33551     },
33552     
33553     /**
33554      * Returns true if this node has been loaded
33555      * @return {Boolean}
33556      */
33557     isLoaded : function(){
33558         return this.loaded;
33559     },
33560     
33561     hasChildNodes : function(){
33562         if(!this.isLeaf() && !this.loaded){
33563             return true;
33564         }else{
33565             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33566         }
33567     },
33568
33569     /**
33570      * Trigger a reload for this node
33571      * @param {Function} callback
33572      */
33573     reload : function(callback){
33574         this.collapse(false, false);
33575         while(this.firstChild){
33576             this.removeChild(this.firstChild);
33577         }
33578         this.childrenRendered = false;
33579         this.loaded = false;
33580         if(this.isHiddenRoot()){
33581             this.expanded = false;
33582         }
33583         this.expand(false, false, callback);
33584     }
33585 });/*
33586  * Based on:
33587  * Ext JS Library 1.1.1
33588  * Copyright(c) 2006-2007, Ext JS, LLC.
33589  *
33590  * Originally Released Under LGPL - original licence link has changed is not relivant.
33591  *
33592  * Fork - LGPL
33593  * <script type="text/javascript">
33594  */
33595  
33596 /**
33597  * @class Roo.tree.TreeNodeUI
33598  * @constructor
33599  * @param {Object} node The node to render
33600  * The TreeNode UI implementation is separate from the
33601  * tree implementation. Unless you are customizing the tree UI,
33602  * you should never have to use this directly.
33603  */
33604 Roo.tree.TreeNodeUI = function(node){
33605     this.node = node;
33606     this.rendered = false;
33607     this.animating = false;
33608     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33609 };
33610
33611 Roo.tree.TreeNodeUI.prototype = {
33612     removeChild : function(node){
33613         if(this.rendered){
33614             this.ctNode.removeChild(node.ui.getEl());
33615         }
33616     },
33617
33618     beforeLoad : function(){
33619          this.addClass("x-tree-node-loading");
33620     },
33621
33622     afterLoad : function(){
33623          this.removeClass("x-tree-node-loading");
33624     },
33625
33626     onTextChange : function(node, text, oldText){
33627         if(this.rendered){
33628             this.textNode.innerHTML = text;
33629         }
33630     },
33631
33632     onDisableChange : function(node, state){
33633         this.disabled = state;
33634         if(state){
33635             this.addClass("x-tree-node-disabled");
33636         }else{
33637             this.removeClass("x-tree-node-disabled");
33638         }
33639     },
33640
33641     onSelectedChange : function(state){
33642         if(state){
33643             this.focus();
33644             this.addClass("x-tree-selected");
33645         }else{
33646             //this.blur();
33647             this.removeClass("x-tree-selected");
33648         }
33649     },
33650
33651     onMove : function(tree, node, oldParent, newParent, index, refNode){
33652         this.childIndent = null;
33653         if(this.rendered){
33654             var targetNode = newParent.ui.getContainer();
33655             if(!targetNode){//target not rendered
33656                 this.holder = document.createElement("div");
33657                 this.holder.appendChild(this.wrap);
33658                 return;
33659             }
33660             var insertBefore = refNode ? refNode.ui.getEl() : null;
33661             if(insertBefore){
33662                 targetNode.insertBefore(this.wrap, insertBefore);
33663             }else{
33664                 targetNode.appendChild(this.wrap);
33665             }
33666             this.node.renderIndent(true);
33667         }
33668     },
33669
33670     addClass : function(cls){
33671         if(this.elNode){
33672             Roo.fly(this.elNode).addClass(cls);
33673         }
33674     },
33675
33676     removeClass : function(cls){
33677         if(this.elNode){
33678             Roo.fly(this.elNode).removeClass(cls);
33679         }
33680     },
33681
33682     remove : function(){
33683         if(this.rendered){
33684             this.holder = document.createElement("div");
33685             this.holder.appendChild(this.wrap);
33686         }
33687     },
33688
33689     fireEvent : function(){
33690         return this.node.fireEvent.apply(this.node, arguments);
33691     },
33692
33693     initEvents : function(){
33694         this.node.on("move", this.onMove, this);
33695         var E = Roo.EventManager;
33696         var a = this.anchor;
33697
33698         var el = Roo.fly(a, '_treeui');
33699
33700         if(Roo.isOpera){ // opera render bug ignores the CSS
33701             el.setStyle("text-decoration", "none");
33702         }
33703
33704         el.on("click", this.onClick, this);
33705         el.on("dblclick", this.onDblClick, this);
33706
33707         if(this.checkbox){
33708             Roo.EventManager.on(this.checkbox,
33709                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33710         }
33711
33712         el.on("contextmenu", this.onContextMenu, this);
33713
33714         var icon = Roo.fly(this.iconNode);
33715         icon.on("click", this.onClick, this);
33716         icon.on("dblclick", this.onDblClick, this);
33717         icon.on("contextmenu", this.onContextMenu, this);
33718         E.on(this.ecNode, "click", this.ecClick, this, true);
33719
33720         if(this.node.disabled){
33721             this.addClass("x-tree-node-disabled");
33722         }
33723         if(this.node.hidden){
33724             this.addClass("x-tree-node-disabled");
33725         }
33726         var ot = this.node.getOwnerTree();
33727         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33728         if(dd && (!this.node.isRoot || ot.rootVisible)){
33729             Roo.dd.Registry.register(this.elNode, {
33730                 node: this.node,
33731                 handles: this.getDDHandles(),
33732                 isHandle: false
33733             });
33734         }
33735     },
33736
33737     getDDHandles : function(){
33738         return [this.iconNode, this.textNode];
33739     },
33740
33741     hide : function(){
33742         if(this.rendered){
33743             this.wrap.style.display = "none";
33744         }
33745     },
33746
33747     show : function(){
33748         if(this.rendered){
33749             this.wrap.style.display = "";
33750         }
33751     },
33752
33753     onContextMenu : function(e){
33754         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33755             e.preventDefault();
33756             this.focus();
33757             this.fireEvent("contextmenu", this.node, e);
33758         }
33759     },
33760
33761     onClick : function(e){
33762         if(this.dropping){
33763             e.stopEvent();
33764             return;
33765         }
33766         if(this.fireEvent("beforeclick", this.node, e) !== false){
33767             if(!this.disabled && this.node.attributes.href){
33768                 this.fireEvent("click", this.node, e);
33769                 return;
33770             }
33771             e.preventDefault();
33772             if(this.disabled){
33773                 return;
33774             }
33775
33776             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33777                 this.node.toggle();
33778             }
33779
33780             this.fireEvent("click", this.node, e);
33781         }else{
33782             e.stopEvent();
33783         }
33784     },
33785
33786     onDblClick : function(e){
33787         e.preventDefault();
33788         if(this.disabled){
33789             return;
33790         }
33791         if(this.checkbox){
33792             this.toggleCheck();
33793         }
33794         if(!this.animating && this.node.hasChildNodes()){
33795             this.node.toggle();
33796         }
33797         this.fireEvent("dblclick", this.node, e);
33798     },
33799
33800     onCheckChange : function(){
33801         var checked = this.checkbox.checked;
33802         this.node.attributes.checked = checked;
33803         this.fireEvent('checkchange', this.node, checked);
33804     },
33805
33806     ecClick : function(e){
33807         if(!this.animating && this.node.hasChildNodes()){
33808             this.node.toggle();
33809         }
33810     },
33811
33812     startDrop : function(){
33813         this.dropping = true;
33814     },
33815
33816     // delayed drop so the click event doesn't get fired on a drop
33817     endDrop : function(){
33818        setTimeout(function(){
33819            this.dropping = false;
33820        }.createDelegate(this), 50);
33821     },
33822
33823     expand : function(){
33824         this.updateExpandIcon();
33825         this.ctNode.style.display = "";
33826     },
33827
33828     focus : function(){
33829         if(!this.node.preventHScroll){
33830             try{this.anchor.focus();
33831             }catch(e){}
33832         }else if(!Roo.isIE){
33833             try{
33834                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33835                 var l = noscroll.scrollLeft;
33836                 this.anchor.focus();
33837                 noscroll.scrollLeft = l;
33838             }catch(e){}
33839         }
33840     },
33841
33842     toggleCheck : function(value){
33843         var cb = this.checkbox;
33844         if(cb){
33845             cb.checked = (value === undefined ? !cb.checked : value);
33846         }
33847     },
33848
33849     blur : function(){
33850         try{
33851             this.anchor.blur();
33852         }catch(e){}
33853     },
33854
33855     animExpand : function(callback){
33856         var ct = Roo.get(this.ctNode);
33857         ct.stopFx();
33858         if(!this.node.hasChildNodes()){
33859             this.updateExpandIcon();
33860             this.ctNode.style.display = "";
33861             Roo.callback(callback);
33862             return;
33863         }
33864         this.animating = true;
33865         this.updateExpandIcon();
33866
33867         ct.slideIn('t', {
33868            callback : function(){
33869                this.animating = false;
33870                Roo.callback(callback);
33871             },
33872             scope: this,
33873             duration: this.node.ownerTree.duration || .25
33874         });
33875     },
33876
33877     highlight : function(){
33878         var tree = this.node.getOwnerTree();
33879         Roo.fly(this.wrap).highlight(
33880             tree.hlColor || "C3DAF9",
33881             {endColor: tree.hlBaseColor}
33882         );
33883     },
33884
33885     collapse : function(){
33886         this.updateExpandIcon();
33887         this.ctNode.style.display = "none";
33888     },
33889
33890     animCollapse : function(callback){
33891         var ct = Roo.get(this.ctNode);
33892         ct.enableDisplayMode('block');
33893         ct.stopFx();
33894
33895         this.animating = true;
33896         this.updateExpandIcon();
33897
33898         ct.slideOut('t', {
33899             callback : function(){
33900                this.animating = false;
33901                Roo.callback(callback);
33902             },
33903             scope: this,
33904             duration: this.node.ownerTree.duration || .25
33905         });
33906     },
33907
33908     getContainer : function(){
33909         return this.ctNode;
33910     },
33911
33912     getEl : function(){
33913         return this.wrap;
33914     },
33915
33916     appendDDGhost : function(ghostNode){
33917         ghostNode.appendChild(this.elNode.cloneNode(true));
33918     },
33919
33920     getDDRepairXY : function(){
33921         return Roo.lib.Dom.getXY(this.iconNode);
33922     },
33923
33924     onRender : function(){
33925         this.render();
33926     },
33927
33928     render : function(bulkRender){
33929         var n = this.node, a = n.attributes;
33930         var targetNode = n.parentNode ?
33931               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33932
33933         if(!this.rendered){
33934             this.rendered = true;
33935
33936             this.renderElements(n, a, targetNode, bulkRender);
33937
33938             if(a.qtip){
33939                if(this.textNode.setAttributeNS){
33940                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33941                    if(a.qtipTitle){
33942                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33943                    }
33944                }else{
33945                    this.textNode.setAttribute("ext:qtip", a.qtip);
33946                    if(a.qtipTitle){
33947                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33948                    }
33949                }
33950             }else if(a.qtipCfg){
33951                 a.qtipCfg.target = Roo.id(this.textNode);
33952                 Roo.QuickTips.register(a.qtipCfg);
33953             }
33954             this.initEvents();
33955             if(!this.node.expanded){
33956                 this.updateExpandIcon();
33957             }
33958         }else{
33959             if(bulkRender === true) {
33960                 targetNode.appendChild(this.wrap);
33961             }
33962         }
33963     },
33964
33965     renderElements : function(n, a, targetNode, bulkRender)
33966     {
33967         // add some indent caching, this helps performance when rendering a large tree
33968         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33969         var t = n.getOwnerTree();
33970         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33971         if (typeof(n.attributes.html) != 'undefined') {
33972             txt = n.attributes.html;
33973         }
33974         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33975         var cb = typeof a.checked == 'boolean';
33976         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33977         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33978             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33979             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33980             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33981             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33982             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33983              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33984                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33985             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33986             "</li>"];
33987
33988         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33989             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33990                                 n.nextSibling.ui.getEl(), buf.join(""));
33991         }else{
33992             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33993         }
33994
33995         this.elNode = this.wrap.childNodes[0];
33996         this.ctNode = this.wrap.childNodes[1];
33997         var cs = this.elNode.childNodes;
33998         this.indentNode = cs[0];
33999         this.ecNode = cs[1];
34000         this.iconNode = cs[2];
34001         var index = 3;
34002         if(cb){
34003             this.checkbox = cs[3];
34004             index++;
34005         }
34006         this.anchor = cs[index];
34007         this.textNode = cs[index].firstChild;
34008     },
34009
34010     getAnchor : function(){
34011         return this.anchor;
34012     },
34013
34014     getTextEl : function(){
34015         return this.textNode;
34016     },
34017
34018     getIconEl : function(){
34019         return this.iconNode;
34020     },
34021
34022     isChecked : function(){
34023         return this.checkbox ? this.checkbox.checked : false;
34024     },
34025
34026     updateExpandIcon : function(){
34027         if(this.rendered){
34028             var n = this.node, c1, c2;
34029             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34030             var hasChild = n.hasChildNodes();
34031             if(hasChild){
34032                 if(n.expanded){
34033                     cls += "-minus";
34034                     c1 = "x-tree-node-collapsed";
34035                     c2 = "x-tree-node-expanded";
34036                 }else{
34037                     cls += "-plus";
34038                     c1 = "x-tree-node-expanded";
34039                     c2 = "x-tree-node-collapsed";
34040                 }
34041                 if(this.wasLeaf){
34042                     this.removeClass("x-tree-node-leaf");
34043                     this.wasLeaf = false;
34044                 }
34045                 if(this.c1 != c1 || this.c2 != c2){
34046                     Roo.fly(this.elNode).replaceClass(c1, c2);
34047                     this.c1 = c1; this.c2 = c2;
34048                 }
34049             }else{
34050                 // this changes non-leafs into leafs if they have no children.
34051                 // it's not very rational behaviour..
34052                 
34053                 if(!this.wasLeaf && this.node.leaf){
34054                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34055                     delete this.c1;
34056                     delete this.c2;
34057                     this.wasLeaf = true;
34058                 }
34059             }
34060             var ecc = "x-tree-ec-icon "+cls;
34061             if(this.ecc != ecc){
34062                 this.ecNode.className = ecc;
34063                 this.ecc = ecc;
34064             }
34065         }
34066     },
34067
34068     getChildIndent : function(){
34069         if(!this.childIndent){
34070             var buf = [];
34071             var p = this.node;
34072             while(p){
34073                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34074                     if(!p.isLast()) {
34075                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34076                     } else {
34077                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34078                     }
34079                 }
34080                 p = p.parentNode;
34081             }
34082             this.childIndent = buf.join("");
34083         }
34084         return this.childIndent;
34085     },
34086
34087     renderIndent : function(){
34088         if(this.rendered){
34089             var indent = "";
34090             var p = this.node.parentNode;
34091             if(p){
34092                 indent = p.ui.getChildIndent();
34093             }
34094             if(this.indentMarkup != indent){ // don't rerender if not required
34095                 this.indentNode.innerHTML = indent;
34096                 this.indentMarkup = indent;
34097             }
34098             this.updateExpandIcon();
34099         }
34100     }
34101 };
34102
34103 Roo.tree.RootTreeNodeUI = function(){
34104     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34105 };
34106 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34107     render : function(){
34108         if(!this.rendered){
34109             var targetNode = this.node.ownerTree.innerCt.dom;
34110             this.node.expanded = true;
34111             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34112             this.wrap = this.ctNode = targetNode.firstChild;
34113         }
34114     },
34115     collapse : function(){
34116     },
34117     expand : function(){
34118     }
34119 });/*
34120  * Based on:
34121  * Ext JS Library 1.1.1
34122  * Copyright(c) 2006-2007, Ext JS, LLC.
34123  *
34124  * Originally Released Under LGPL - original licence link has changed is not relivant.
34125  *
34126  * Fork - LGPL
34127  * <script type="text/javascript">
34128  */
34129 /**
34130  * @class Roo.tree.TreeLoader
34131  * @extends Roo.util.Observable
34132  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34133  * nodes from a specified URL. The response must be a javascript Array definition
34134  * who's elements are node definition objects. eg:
34135  * <pre><code>
34136 {  success : true,
34137    data :      [
34138    
34139     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34140     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34141     ]
34142 }
34143
34144
34145 </code></pre>
34146  * <br><br>
34147  * The old style respose with just an array is still supported, but not recommended.
34148  * <br><br>
34149  *
34150  * A server request is sent, and child nodes are loaded only when a node is expanded.
34151  * The loading node's id is passed to the server under the parameter name "node" to
34152  * enable the server to produce the correct child nodes.
34153  * <br><br>
34154  * To pass extra parameters, an event handler may be attached to the "beforeload"
34155  * event, and the parameters specified in the TreeLoader's baseParams property:
34156  * <pre><code>
34157     myTreeLoader.on("beforeload", function(treeLoader, node) {
34158         this.baseParams.category = node.attributes.category;
34159     }, this);
34160 </code></pre><
34161  * This would pass an HTTP parameter called "category" to the server containing
34162  * the value of the Node's "category" attribute.
34163  * @constructor
34164  * Creates a new Treeloader.
34165  * @param {Object} config A config object containing config properties.
34166  */
34167 Roo.tree.TreeLoader = function(config){
34168     this.baseParams = {};
34169     this.requestMethod = "POST";
34170     Roo.apply(this, config);
34171
34172     this.addEvents({
34173     
34174         /**
34175          * @event beforeload
34176          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34177          * @param {Object} This TreeLoader object.
34178          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34179          * @param {Object} callback The callback function specified in the {@link #load} call.
34180          */
34181         beforeload : true,
34182         /**
34183          * @event load
34184          * Fires when the node has been successfuly loaded.
34185          * @param {Object} This TreeLoader object.
34186          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34187          * @param {Object} response The response object containing the data from the server.
34188          */
34189         load : true,
34190         /**
34191          * @event loadexception
34192          * Fires if the network request failed.
34193          * @param {Object} This TreeLoader object.
34194          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34195          * @param {Object} response The response object containing the data from the server.
34196          */
34197         loadexception : true,
34198         /**
34199          * @event create
34200          * Fires before a node is created, enabling you to return custom Node types 
34201          * @param {Object} This TreeLoader object.
34202          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34203          */
34204         create : true
34205     });
34206
34207     Roo.tree.TreeLoader.superclass.constructor.call(this);
34208 };
34209
34210 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34211     /**
34212     * @cfg {String} dataUrl The URL from which to request a Json string which
34213     * specifies an array of node definition object representing the child nodes
34214     * to be loaded.
34215     */
34216     /**
34217     * @cfg {String} requestMethod either GET or POST
34218     * defaults to POST (due to BC)
34219     * to be loaded.
34220     */
34221     /**
34222     * @cfg {Object} baseParams (optional) An object containing properties which
34223     * specify HTTP parameters to be passed to each request for child nodes.
34224     */
34225     /**
34226     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34227     * created by this loader. If the attributes sent by the server have an attribute in this object,
34228     * they take priority.
34229     */
34230     /**
34231     * @cfg {Object} uiProviders (optional) An object containing properties which
34232     * 
34233     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34234     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34235     * <i>uiProvider</i> attribute of a returned child node is a string rather
34236     * than a reference to a TreeNodeUI implementation, this that string value
34237     * is used as a property name in the uiProviders object. You can define the provider named
34238     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34239     */
34240     uiProviders : {},
34241
34242     /**
34243     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34244     * child nodes before loading.
34245     */
34246     clearOnLoad : true,
34247
34248     /**
34249     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34250     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34251     * Grid query { data : [ .....] }
34252     */
34253     
34254     root : false,
34255      /**
34256     * @cfg {String} queryParam (optional) 
34257     * Name of the query as it will be passed on the querystring (defaults to 'node')
34258     * eg. the request will be ?node=[id]
34259     */
34260     
34261     
34262     queryParam: false,
34263     
34264     /**
34265      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34266      * This is called automatically when a node is expanded, but may be used to reload
34267      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34268      * @param {Roo.tree.TreeNode} node
34269      * @param {Function} callback
34270      */
34271     load : function(node, callback){
34272         if(this.clearOnLoad){
34273             while(node.firstChild){
34274                 node.removeChild(node.firstChild);
34275             }
34276         }
34277         if(node.attributes.children){ // preloaded json children
34278             var cs = node.attributes.children;
34279             for(var i = 0, len = cs.length; i < len; i++){
34280                 node.appendChild(this.createNode(cs[i]));
34281             }
34282             if(typeof callback == "function"){
34283                 callback();
34284             }
34285         }else if(this.dataUrl){
34286             this.requestData(node, callback);
34287         }
34288     },
34289
34290     getParams: function(node){
34291         var buf = [], bp = this.baseParams;
34292         for(var key in bp){
34293             if(typeof bp[key] != "function"){
34294                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34295             }
34296         }
34297         var n = this.queryParam === false ? 'node' : this.queryParam;
34298         buf.push(n + "=", encodeURIComponent(node.id));
34299         return buf.join("");
34300     },
34301
34302     requestData : function(node, callback){
34303         if(this.fireEvent("beforeload", this, node, callback) !== false){
34304             this.transId = Roo.Ajax.request({
34305                 method:this.requestMethod,
34306                 url: this.dataUrl||this.url,
34307                 success: this.handleResponse,
34308                 failure: this.handleFailure,
34309                 scope: this,
34310                 argument: {callback: callback, node: node},
34311                 params: this.getParams(node)
34312             });
34313         }else{
34314             // if the load is cancelled, make sure we notify
34315             // the node that we are done
34316             if(typeof callback == "function"){
34317                 callback();
34318             }
34319         }
34320     },
34321
34322     isLoading : function(){
34323         return this.transId ? true : false;
34324     },
34325
34326     abort : function(){
34327         if(this.isLoading()){
34328             Roo.Ajax.abort(this.transId);
34329         }
34330     },
34331
34332     // private
34333     createNode : function(attr)
34334     {
34335         // apply baseAttrs, nice idea Corey!
34336         if(this.baseAttrs){
34337             Roo.applyIf(attr, this.baseAttrs);
34338         }
34339         if(this.applyLoader !== false){
34340             attr.loader = this;
34341         }
34342         // uiProvider = depreciated..
34343         
34344         if(typeof(attr.uiProvider) == 'string'){
34345            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34346                 /**  eval:var:attr */ eval(attr.uiProvider);
34347         }
34348         if(typeof(this.uiProviders['default']) != 'undefined') {
34349             attr.uiProvider = this.uiProviders['default'];
34350         }
34351         
34352         this.fireEvent('create', this, attr);
34353         
34354         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34355         return(attr.leaf ?
34356                         new Roo.tree.TreeNode(attr) :
34357                         new Roo.tree.AsyncTreeNode(attr));
34358     },
34359
34360     processResponse : function(response, node, callback)
34361     {
34362         var json = response.responseText;
34363         try {
34364             
34365             var o = Roo.decode(json);
34366             
34367             if (this.root === false && typeof(o.success) != undefined) {
34368                 this.root = 'data'; // the default behaviour for list like data..
34369                 }
34370                 
34371             if (this.root !== false &&  !o.success) {
34372                 // it's a failure condition.
34373                 var a = response.argument;
34374                 this.fireEvent("loadexception", this, a.node, response);
34375                 Roo.log("Load failed - should have a handler really");
34376                 return;
34377             }
34378             
34379             
34380             
34381             if (this.root !== false) {
34382                  o = o[this.root];
34383             }
34384             
34385             for(var i = 0, len = o.length; i < len; i++){
34386                 var n = this.createNode(o[i]);
34387                 if(n){
34388                     node.appendChild(n);
34389                 }
34390             }
34391             if(typeof callback == "function"){
34392                 callback(this, node);
34393             }
34394         }catch(e){
34395             this.handleFailure(response);
34396         }
34397     },
34398
34399     handleResponse : function(response){
34400         this.transId = false;
34401         var a = response.argument;
34402         this.processResponse(response, a.node, a.callback);
34403         this.fireEvent("load", this, a.node, response);
34404     },
34405
34406     handleFailure : function(response)
34407     {
34408         // should handle failure better..
34409         this.transId = false;
34410         var a = response.argument;
34411         this.fireEvent("loadexception", this, a.node, response);
34412         if(typeof a.callback == "function"){
34413             a.callback(this, a.node);
34414         }
34415     }
34416 });/*
34417  * Based on:
34418  * Ext JS Library 1.1.1
34419  * Copyright(c) 2006-2007, Ext JS, LLC.
34420  *
34421  * Originally Released Under LGPL - original licence link has changed is not relivant.
34422  *
34423  * Fork - LGPL
34424  * <script type="text/javascript">
34425  */
34426
34427 /**
34428 * @class Roo.tree.TreeFilter
34429 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34430 * @param {TreePanel} tree
34431 * @param {Object} config (optional)
34432  */
34433 Roo.tree.TreeFilter = function(tree, config){
34434     this.tree = tree;
34435     this.filtered = {};
34436     Roo.apply(this, config);
34437 };
34438
34439 Roo.tree.TreeFilter.prototype = {
34440     clearBlank:false,
34441     reverse:false,
34442     autoClear:false,
34443     remove:false,
34444
34445      /**
34446      * Filter the data by a specific attribute.
34447      * @param {String/RegExp} value Either string that the attribute value
34448      * should start with or a RegExp to test against the attribute
34449      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34450      * @param {TreeNode} startNode (optional) The node to start the filter at.
34451      */
34452     filter : function(value, attr, startNode){
34453         attr = attr || "text";
34454         var f;
34455         if(typeof value == "string"){
34456             var vlen = value.length;
34457             // auto clear empty filter
34458             if(vlen == 0 && this.clearBlank){
34459                 this.clear();
34460                 return;
34461             }
34462             value = value.toLowerCase();
34463             f = function(n){
34464                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34465             };
34466         }else if(value.exec){ // regex?
34467             f = function(n){
34468                 return value.test(n.attributes[attr]);
34469             };
34470         }else{
34471             throw 'Illegal filter type, must be string or regex';
34472         }
34473         this.filterBy(f, null, startNode);
34474         },
34475
34476     /**
34477      * Filter by a function. The passed function will be called with each
34478      * node in the tree (or from the startNode). If the function returns true, the node is kept
34479      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34480      * @param {Function} fn The filter function
34481      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34482      */
34483     filterBy : function(fn, scope, startNode){
34484         startNode = startNode || this.tree.root;
34485         if(this.autoClear){
34486             this.clear();
34487         }
34488         var af = this.filtered, rv = this.reverse;
34489         var f = function(n){
34490             if(n == startNode){
34491                 return true;
34492             }
34493             if(af[n.id]){
34494                 return false;
34495             }
34496             var m = fn.call(scope || n, n);
34497             if(!m || rv){
34498                 af[n.id] = n;
34499                 n.ui.hide();
34500                 return false;
34501             }
34502             return true;
34503         };
34504         startNode.cascade(f);
34505         if(this.remove){
34506            for(var id in af){
34507                if(typeof id != "function"){
34508                    var n = af[id];
34509                    if(n && n.parentNode){
34510                        n.parentNode.removeChild(n);
34511                    }
34512                }
34513            }
34514         }
34515     },
34516
34517     /**
34518      * Clears the current filter. Note: with the "remove" option
34519      * set a filter cannot be cleared.
34520      */
34521     clear : function(){
34522         var t = this.tree;
34523         var af = this.filtered;
34524         for(var id in af){
34525             if(typeof id != "function"){
34526                 var n = af[id];
34527                 if(n){
34528                     n.ui.show();
34529                 }
34530             }
34531         }
34532         this.filtered = {};
34533     }
34534 };
34535 /*
34536  * Based on:
34537  * Ext JS Library 1.1.1
34538  * Copyright(c) 2006-2007, Ext JS, LLC.
34539  *
34540  * Originally Released Under LGPL - original licence link has changed is not relivant.
34541  *
34542  * Fork - LGPL
34543  * <script type="text/javascript">
34544  */
34545  
34546
34547 /**
34548  * @class Roo.tree.TreeSorter
34549  * Provides sorting of nodes in a TreePanel
34550  * 
34551  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34552  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34553  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34554  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34555  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34556  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34557  * @constructor
34558  * @param {TreePanel} tree
34559  * @param {Object} config
34560  */
34561 Roo.tree.TreeSorter = function(tree, config){
34562     Roo.apply(this, config);
34563     tree.on("beforechildrenrendered", this.doSort, this);
34564     tree.on("append", this.updateSort, this);
34565     tree.on("insert", this.updateSort, this);
34566     
34567     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34568     var p = this.property || "text";
34569     var sortType = this.sortType;
34570     var fs = this.folderSort;
34571     var cs = this.caseSensitive === true;
34572     var leafAttr = this.leafAttr || 'leaf';
34573
34574     this.sortFn = function(n1, n2){
34575         if(fs){
34576             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34577                 return 1;
34578             }
34579             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34580                 return -1;
34581             }
34582         }
34583         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34584         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34585         if(v1 < v2){
34586                         return dsc ? +1 : -1;
34587                 }else if(v1 > v2){
34588                         return dsc ? -1 : +1;
34589         }else{
34590                 return 0;
34591         }
34592     };
34593 };
34594
34595 Roo.tree.TreeSorter.prototype = {
34596     doSort : function(node){
34597         node.sort(this.sortFn);
34598     },
34599     
34600     compareNodes : function(n1, n2){
34601         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34602     },
34603     
34604     updateSort : function(tree, node){
34605         if(node.childrenRendered){
34606             this.doSort.defer(1, this, [node]);
34607         }
34608     }
34609 };/*
34610  * Based on:
34611  * Ext JS Library 1.1.1
34612  * Copyright(c) 2006-2007, Ext JS, LLC.
34613  *
34614  * Originally Released Under LGPL - original licence link has changed is not relivant.
34615  *
34616  * Fork - LGPL
34617  * <script type="text/javascript">
34618  */
34619
34620 if(Roo.dd.DropZone){
34621     
34622 Roo.tree.TreeDropZone = function(tree, config){
34623     this.allowParentInsert = false;
34624     this.allowContainerDrop = false;
34625     this.appendOnly = false;
34626     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34627     this.tree = tree;
34628     this.lastInsertClass = "x-tree-no-status";
34629     this.dragOverData = {};
34630 };
34631
34632 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34633     ddGroup : "TreeDD",
34634     scroll:  true,
34635     
34636     expandDelay : 1000,
34637     
34638     expandNode : function(node){
34639         if(node.hasChildNodes() && !node.isExpanded()){
34640             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34641         }
34642     },
34643     
34644     queueExpand : function(node){
34645         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34646     },
34647     
34648     cancelExpand : function(){
34649         if(this.expandProcId){
34650             clearTimeout(this.expandProcId);
34651             this.expandProcId = false;
34652         }
34653     },
34654     
34655     isValidDropPoint : function(n, pt, dd, e, data){
34656         if(!n || !data){ return false; }
34657         var targetNode = n.node;
34658         var dropNode = data.node;
34659         // default drop rules
34660         if(!(targetNode && targetNode.isTarget && pt)){
34661             return false;
34662         }
34663         if(pt == "append" && targetNode.allowChildren === false){
34664             return false;
34665         }
34666         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34667             return false;
34668         }
34669         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34670             return false;
34671         }
34672         // reuse the object
34673         var overEvent = this.dragOverData;
34674         overEvent.tree = this.tree;
34675         overEvent.target = targetNode;
34676         overEvent.data = data;
34677         overEvent.point = pt;
34678         overEvent.source = dd;
34679         overEvent.rawEvent = e;
34680         overEvent.dropNode = dropNode;
34681         overEvent.cancel = false;  
34682         var result = this.tree.fireEvent("nodedragover", overEvent);
34683         return overEvent.cancel === false && result !== false;
34684     },
34685     
34686     getDropPoint : function(e, n, dd)
34687     {
34688         var tn = n.node;
34689         if(tn.isRoot){
34690             return tn.allowChildren !== false ? "append" : false; // always append for root
34691         }
34692         var dragEl = n.ddel;
34693         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34694         var y = Roo.lib.Event.getPageY(e);
34695         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34696         
34697         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34698         var noAppend = tn.allowChildren === false;
34699         if(this.appendOnly || tn.parentNode.allowChildren === false){
34700             return noAppend ? false : "append";
34701         }
34702         var noBelow = false;
34703         if(!this.allowParentInsert){
34704             noBelow = tn.hasChildNodes() && tn.isExpanded();
34705         }
34706         var q = (b - t) / (noAppend ? 2 : 3);
34707         if(y >= t && y < (t + q)){
34708             return "above";
34709         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34710             return "below";
34711         }else{
34712             return "append";
34713         }
34714     },
34715     
34716     onNodeEnter : function(n, dd, e, data)
34717     {
34718         this.cancelExpand();
34719     },
34720     
34721     onNodeOver : function(n, dd, e, data)
34722     {
34723        
34724         var pt = this.getDropPoint(e, n, dd);
34725         var node = n.node;
34726         
34727         // auto node expand check
34728         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34729             this.queueExpand(node);
34730         }else if(pt != "append"){
34731             this.cancelExpand();
34732         }
34733         
34734         // set the insert point style on the target node
34735         var returnCls = this.dropNotAllowed;
34736         if(this.isValidDropPoint(n, pt, dd, e, data)){
34737            if(pt){
34738                var el = n.ddel;
34739                var cls;
34740                if(pt == "above"){
34741                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34742                    cls = "x-tree-drag-insert-above";
34743                }else if(pt == "below"){
34744                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34745                    cls = "x-tree-drag-insert-below";
34746                }else{
34747                    returnCls = "x-tree-drop-ok-append";
34748                    cls = "x-tree-drag-append";
34749                }
34750                if(this.lastInsertClass != cls){
34751                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34752                    this.lastInsertClass = cls;
34753                }
34754            }
34755        }
34756        return returnCls;
34757     },
34758     
34759     onNodeOut : function(n, dd, e, data){
34760         
34761         this.cancelExpand();
34762         this.removeDropIndicators(n);
34763     },
34764     
34765     onNodeDrop : function(n, dd, e, data){
34766         var point = this.getDropPoint(e, n, dd);
34767         var targetNode = n.node;
34768         targetNode.ui.startDrop();
34769         if(!this.isValidDropPoint(n, point, dd, e, data)){
34770             targetNode.ui.endDrop();
34771             return false;
34772         }
34773         // first try to find the drop node
34774         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34775         var dropEvent = {
34776             tree : this.tree,
34777             target: targetNode,
34778             data: data,
34779             point: point,
34780             source: dd,
34781             rawEvent: e,
34782             dropNode: dropNode,
34783             cancel: !dropNode   
34784         };
34785         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34786         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34787             targetNode.ui.endDrop();
34788             return false;
34789         }
34790         // allow target changing
34791         targetNode = dropEvent.target;
34792         if(point == "append" && !targetNode.isExpanded()){
34793             targetNode.expand(false, null, function(){
34794                 this.completeDrop(dropEvent);
34795             }.createDelegate(this));
34796         }else{
34797             this.completeDrop(dropEvent);
34798         }
34799         return true;
34800     },
34801     
34802     completeDrop : function(de){
34803         var ns = de.dropNode, p = de.point, t = de.target;
34804         if(!(ns instanceof Array)){
34805             ns = [ns];
34806         }
34807         var n;
34808         for(var i = 0, len = ns.length; i < len; i++){
34809             n = ns[i];
34810             if(p == "above"){
34811                 t.parentNode.insertBefore(n, t);
34812             }else if(p == "below"){
34813                 t.parentNode.insertBefore(n, t.nextSibling);
34814             }else{
34815                 t.appendChild(n);
34816             }
34817         }
34818         n.ui.focus();
34819         if(this.tree.hlDrop){
34820             n.ui.highlight();
34821         }
34822         t.ui.endDrop();
34823         this.tree.fireEvent("nodedrop", de);
34824     },
34825     
34826     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34827         if(this.tree.hlDrop){
34828             dropNode.ui.focus();
34829             dropNode.ui.highlight();
34830         }
34831         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34832     },
34833     
34834     getTree : function(){
34835         return this.tree;
34836     },
34837     
34838     removeDropIndicators : function(n){
34839         if(n && n.ddel){
34840             var el = n.ddel;
34841             Roo.fly(el).removeClass([
34842                     "x-tree-drag-insert-above",
34843                     "x-tree-drag-insert-below",
34844                     "x-tree-drag-append"]);
34845             this.lastInsertClass = "_noclass";
34846         }
34847     },
34848     
34849     beforeDragDrop : function(target, e, id){
34850         this.cancelExpand();
34851         return true;
34852     },
34853     
34854     afterRepair : function(data){
34855         if(data && Roo.enableFx){
34856             data.node.ui.highlight();
34857         }
34858         this.hideProxy();
34859     } 
34860     
34861 });
34862
34863 }
34864 /*
34865  * Based on:
34866  * Ext JS Library 1.1.1
34867  * Copyright(c) 2006-2007, Ext JS, LLC.
34868  *
34869  * Originally Released Under LGPL - original licence link has changed is not relivant.
34870  *
34871  * Fork - LGPL
34872  * <script type="text/javascript">
34873  */
34874  
34875
34876 if(Roo.dd.DragZone){
34877 Roo.tree.TreeDragZone = function(tree, config){
34878     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34879     this.tree = tree;
34880 };
34881
34882 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34883     ddGroup : "TreeDD",
34884    
34885     onBeforeDrag : function(data, e){
34886         var n = data.node;
34887         return n && n.draggable && !n.disabled;
34888     },
34889      
34890     
34891     onInitDrag : function(e){
34892         var data = this.dragData;
34893         this.tree.getSelectionModel().select(data.node);
34894         this.proxy.update("");
34895         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34896         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34897     },
34898     
34899     getRepairXY : function(e, data){
34900         return data.node.ui.getDDRepairXY();
34901     },
34902     
34903     onEndDrag : function(data, e){
34904         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34905         
34906         
34907     },
34908     
34909     onValidDrop : function(dd, e, id){
34910         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34911         this.hideProxy();
34912     },
34913     
34914     beforeInvalidDrop : function(e, id){
34915         // this scrolls the original position back into view
34916         var sm = this.tree.getSelectionModel();
34917         sm.clearSelections();
34918         sm.select(this.dragData.node);
34919     }
34920 });
34921 }/*
34922  * Based on:
34923  * Ext JS Library 1.1.1
34924  * Copyright(c) 2006-2007, Ext JS, LLC.
34925  *
34926  * Originally Released Under LGPL - original licence link has changed is not relivant.
34927  *
34928  * Fork - LGPL
34929  * <script type="text/javascript">
34930  */
34931 /**
34932  * @class Roo.tree.TreeEditor
34933  * @extends Roo.Editor
34934  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34935  * as the editor field.
34936  * @constructor
34937  * @param {Object} config (used to be the tree panel.)
34938  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34939  * 
34940  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34941  * @cfg {Roo.form.TextField|Object} field The field configuration
34942  *
34943  * 
34944  */
34945 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34946     var tree = config;
34947     var field;
34948     if (oldconfig) { // old style..
34949         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34950     } else {
34951         // new style..
34952         tree = config.tree;
34953         config.field = config.field  || {};
34954         config.field.xtype = 'TextField';
34955         field = Roo.factory(config.field, Roo.form);
34956     }
34957     config = config || {};
34958     
34959     
34960     this.addEvents({
34961         /**
34962          * @event beforenodeedit
34963          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34964          * false from the handler of this event.
34965          * @param {Editor} this
34966          * @param {Roo.tree.Node} node 
34967          */
34968         "beforenodeedit" : true
34969     });
34970     
34971     //Roo.log(config);
34972     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34973
34974     this.tree = tree;
34975
34976     tree.on('beforeclick', this.beforeNodeClick, this);
34977     tree.getTreeEl().on('mousedown', this.hide, this);
34978     this.on('complete', this.updateNode, this);
34979     this.on('beforestartedit', this.fitToTree, this);
34980     this.on('startedit', this.bindScroll, this, {delay:10});
34981     this.on('specialkey', this.onSpecialKey, this);
34982 };
34983
34984 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34985     /**
34986      * @cfg {String} alignment
34987      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34988      */
34989     alignment: "l-l",
34990     // inherit
34991     autoSize: false,
34992     /**
34993      * @cfg {Boolean} hideEl
34994      * True to hide the bound element while the editor is displayed (defaults to false)
34995      */
34996     hideEl : false,
34997     /**
34998      * @cfg {String} cls
34999      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35000      */
35001     cls: "x-small-editor x-tree-editor",
35002     /**
35003      * @cfg {Boolean} shim
35004      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35005      */
35006     shim:false,
35007     // inherit
35008     shadow:"frame",
35009     /**
35010      * @cfg {Number} maxWidth
35011      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35012      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35013      * scroll and client offsets into account prior to each edit.
35014      */
35015     maxWidth: 250,
35016
35017     editDelay : 350,
35018
35019     // private
35020     fitToTree : function(ed, el){
35021         var td = this.tree.getTreeEl().dom, nd = el.dom;
35022         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35023             td.scrollLeft = nd.offsetLeft;
35024         }
35025         var w = Math.min(
35026                 this.maxWidth,
35027                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35028         this.setSize(w, '');
35029         
35030         return this.fireEvent('beforenodeedit', this, this.editNode);
35031         
35032     },
35033
35034     // private
35035     triggerEdit : function(node){
35036         this.completeEdit();
35037         this.editNode = node;
35038         this.startEdit(node.ui.textNode, node.text);
35039     },
35040
35041     // private
35042     bindScroll : function(){
35043         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35044     },
35045
35046     // private
35047     beforeNodeClick : function(node, e){
35048         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35049         this.lastClick = new Date();
35050         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35051             e.stopEvent();
35052             this.triggerEdit(node);
35053             return false;
35054         }
35055         return true;
35056     },
35057
35058     // private
35059     updateNode : function(ed, value){
35060         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35061         this.editNode.setText(value);
35062     },
35063
35064     // private
35065     onHide : function(){
35066         Roo.tree.TreeEditor.superclass.onHide.call(this);
35067         if(this.editNode){
35068             this.editNode.ui.focus();
35069         }
35070     },
35071
35072     // private
35073     onSpecialKey : function(field, e){
35074         var k = e.getKey();
35075         if(k == e.ESC){
35076             e.stopEvent();
35077             this.cancelEdit();
35078         }else if(k == e.ENTER && !e.hasModifier()){
35079             e.stopEvent();
35080             this.completeEdit();
35081         }
35082     }
35083 });//<Script type="text/javascript">
35084 /*
35085  * Based on:
35086  * Ext JS Library 1.1.1
35087  * Copyright(c) 2006-2007, Ext JS, LLC.
35088  *
35089  * Originally Released Under LGPL - original licence link has changed is not relivant.
35090  *
35091  * Fork - LGPL
35092  * <script type="text/javascript">
35093  */
35094  
35095 /**
35096  * Not documented??? - probably should be...
35097  */
35098
35099 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35100     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35101     
35102     renderElements : function(n, a, targetNode, bulkRender){
35103         //consel.log("renderElements?");
35104         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35105
35106         var t = n.getOwnerTree();
35107         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35108         
35109         var cols = t.columns;
35110         var bw = t.borderWidth;
35111         var c = cols[0];
35112         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35113          var cb = typeof a.checked == "boolean";
35114         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35115         var colcls = 'x-t-' + tid + '-c0';
35116         var buf = [
35117             '<li class="x-tree-node">',
35118             
35119                 
35120                 '<div class="x-tree-node-el ', a.cls,'">',
35121                     // extran...
35122                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35123                 
35124                 
35125                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35126                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35127                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35128                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35129                            (a.iconCls ? ' '+a.iconCls : ''),
35130                            '" unselectable="on" />',
35131                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35132                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35133                              
35134                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35135                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35136                             '<span unselectable="on" qtip="' + tx + '">',
35137                              tx,
35138                              '</span></a>' ,
35139                     '</div>',
35140                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35141                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35142                  ];
35143         for(var i = 1, len = cols.length; i < len; i++){
35144             c = cols[i];
35145             colcls = 'x-t-' + tid + '-c' +i;
35146             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35147             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35148                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35149                       "</div>");
35150          }
35151          
35152          buf.push(
35153             '</a>',
35154             '<div class="x-clear"></div></div>',
35155             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35156             "</li>");
35157         
35158         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35159             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35160                                 n.nextSibling.ui.getEl(), buf.join(""));
35161         }else{
35162             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35163         }
35164         var el = this.wrap.firstChild;
35165         this.elRow = el;
35166         this.elNode = el.firstChild;
35167         this.ranchor = el.childNodes[1];
35168         this.ctNode = this.wrap.childNodes[1];
35169         var cs = el.firstChild.childNodes;
35170         this.indentNode = cs[0];
35171         this.ecNode = cs[1];
35172         this.iconNode = cs[2];
35173         var index = 3;
35174         if(cb){
35175             this.checkbox = cs[3];
35176             index++;
35177         }
35178         this.anchor = cs[index];
35179         
35180         this.textNode = cs[index].firstChild;
35181         
35182         //el.on("click", this.onClick, this);
35183         //el.on("dblclick", this.onDblClick, this);
35184         
35185         
35186        // console.log(this);
35187     },
35188     initEvents : function(){
35189         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35190         
35191             
35192         var a = this.ranchor;
35193
35194         var el = Roo.get(a);
35195
35196         if(Roo.isOpera){ // opera render bug ignores the CSS
35197             el.setStyle("text-decoration", "none");
35198         }
35199
35200         el.on("click", this.onClick, this);
35201         el.on("dblclick", this.onDblClick, this);
35202         el.on("contextmenu", this.onContextMenu, this);
35203         
35204     },
35205     
35206     /*onSelectedChange : function(state){
35207         if(state){
35208             this.focus();
35209             this.addClass("x-tree-selected");
35210         }else{
35211             //this.blur();
35212             this.removeClass("x-tree-selected");
35213         }
35214     },*/
35215     addClass : function(cls){
35216         if(this.elRow){
35217             Roo.fly(this.elRow).addClass(cls);
35218         }
35219         
35220     },
35221     
35222     
35223     removeClass : function(cls){
35224         if(this.elRow){
35225             Roo.fly(this.elRow).removeClass(cls);
35226         }
35227     }
35228
35229     
35230     
35231 });//<Script type="text/javascript">
35232
35233 /*
35234  * Based on:
35235  * Ext JS Library 1.1.1
35236  * Copyright(c) 2006-2007, Ext JS, LLC.
35237  *
35238  * Originally Released Under LGPL - original licence link has changed is not relivant.
35239  *
35240  * Fork - LGPL
35241  * <script type="text/javascript">
35242  */
35243  
35244
35245 /**
35246  * @class Roo.tree.ColumnTree
35247  * @extends Roo.data.TreePanel
35248  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35249  * @cfg {int} borderWidth  compined right/left border allowance
35250  * @constructor
35251  * @param {String/HTMLElement/Element} el The container element
35252  * @param {Object} config
35253  */
35254 Roo.tree.ColumnTree =  function(el, config)
35255 {
35256    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35257    this.addEvents({
35258         /**
35259         * @event resize
35260         * Fire this event on a container when it resizes
35261         * @param {int} w Width
35262         * @param {int} h Height
35263         */
35264        "resize" : true
35265     });
35266     this.on('resize', this.onResize, this);
35267 };
35268
35269 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35270     //lines:false,
35271     
35272     
35273     borderWidth: Roo.isBorderBox ? 0 : 2, 
35274     headEls : false,
35275     
35276     render : function(){
35277         // add the header.....
35278        
35279         Roo.tree.ColumnTree.superclass.render.apply(this);
35280         
35281         this.el.addClass('x-column-tree');
35282         
35283         this.headers = this.el.createChild(
35284             {cls:'x-tree-headers'},this.innerCt.dom);
35285    
35286         var cols = this.columns, c;
35287         var totalWidth = 0;
35288         this.headEls = [];
35289         var  len = cols.length;
35290         for(var i = 0; i < len; i++){
35291              c = cols[i];
35292              totalWidth += c.width;
35293             this.headEls.push(this.headers.createChild({
35294                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35295                  cn: {
35296                      cls:'x-tree-hd-text',
35297                      html: c.header
35298                  },
35299                  style:'width:'+(c.width-this.borderWidth)+'px;'
35300              }));
35301         }
35302         this.headers.createChild({cls:'x-clear'});
35303         // prevent floats from wrapping when clipped
35304         this.headers.setWidth(totalWidth);
35305         //this.innerCt.setWidth(totalWidth);
35306         this.innerCt.setStyle({ overflow: 'auto' });
35307         this.onResize(this.width, this.height);
35308              
35309         
35310     },
35311     onResize : function(w,h)
35312     {
35313         this.height = h;
35314         this.width = w;
35315         // resize cols..
35316         this.innerCt.setWidth(this.width);
35317         this.innerCt.setHeight(this.height-20);
35318         
35319         // headers...
35320         var cols = this.columns, c;
35321         var totalWidth = 0;
35322         var expEl = false;
35323         var len = cols.length;
35324         for(var i = 0; i < len; i++){
35325             c = cols[i];
35326             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35327                 // it's the expander..
35328                 expEl  = this.headEls[i];
35329                 continue;
35330             }
35331             totalWidth += c.width;
35332             
35333         }
35334         if (expEl) {
35335             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35336         }
35337         this.headers.setWidth(w-20);
35338
35339         
35340         
35341         
35342     }
35343 });
35344 /*
35345  * Based on:
35346  * Ext JS Library 1.1.1
35347  * Copyright(c) 2006-2007, Ext JS, LLC.
35348  *
35349  * Originally Released Under LGPL - original licence link has changed is not relivant.
35350  *
35351  * Fork - LGPL
35352  * <script type="text/javascript">
35353  */
35354  
35355 /**
35356  * @class Roo.menu.Menu
35357  * @extends Roo.util.Observable
35358  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35359  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35360  * @constructor
35361  * Creates a new Menu
35362  * @param {Object} config Configuration options
35363  */
35364 Roo.menu.Menu = function(config){
35365     Roo.apply(this, config);
35366     this.id = this.id || Roo.id();
35367     this.addEvents({
35368         /**
35369          * @event beforeshow
35370          * Fires before this menu is displayed
35371          * @param {Roo.menu.Menu} this
35372          */
35373         beforeshow : true,
35374         /**
35375          * @event beforehide
35376          * Fires before this menu is hidden
35377          * @param {Roo.menu.Menu} this
35378          */
35379         beforehide : true,
35380         /**
35381          * @event show
35382          * Fires after this menu is displayed
35383          * @param {Roo.menu.Menu} this
35384          */
35385         show : true,
35386         /**
35387          * @event hide
35388          * Fires after this menu is hidden
35389          * @param {Roo.menu.Menu} this
35390          */
35391         hide : true,
35392         /**
35393          * @event click
35394          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35395          * @param {Roo.menu.Menu} this
35396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35397          * @param {Roo.EventObject} e
35398          */
35399         click : true,
35400         /**
35401          * @event mouseover
35402          * Fires when the mouse is hovering over this menu
35403          * @param {Roo.menu.Menu} this
35404          * @param {Roo.EventObject} e
35405          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35406          */
35407         mouseover : true,
35408         /**
35409          * @event mouseout
35410          * Fires when the mouse exits this menu
35411          * @param {Roo.menu.Menu} this
35412          * @param {Roo.EventObject} e
35413          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35414          */
35415         mouseout : true,
35416         /**
35417          * @event itemclick
35418          * Fires when a menu item contained in this menu is clicked
35419          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35420          * @param {Roo.EventObject} e
35421          */
35422         itemclick: true
35423     });
35424     if (this.registerMenu) {
35425         Roo.menu.MenuMgr.register(this);
35426     }
35427     
35428     var mis = this.items;
35429     this.items = new Roo.util.MixedCollection();
35430     if(mis){
35431         this.add.apply(this, mis);
35432     }
35433 };
35434
35435 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35436     /**
35437      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35438      */
35439     minWidth : 120,
35440     /**
35441      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35442      * for bottom-right shadow (defaults to "sides")
35443      */
35444     shadow : "sides",
35445     /**
35446      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35447      * this menu (defaults to "tl-tr?")
35448      */
35449     subMenuAlign : "tl-tr?",
35450     /**
35451      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35452      * relative to its element of origin (defaults to "tl-bl?")
35453      */
35454     defaultAlign : "tl-bl?",
35455     /**
35456      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35457      */
35458     allowOtherMenus : false,
35459     /**
35460      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35461      */
35462     registerMenu : true,
35463
35464     hidden:true,
35465
35466     // private
35467     render : function(){
35468         if(this.el){
35469             return;
35470         }
35471         var el = this.el = new Roo.Layer({
35472             cls: "x-menu",
35473             shadow:this.shadow,
35474             constrain: false,
35475             parentEl: this.parentEl || document.body,
35476             zindex:15000
35477         });
35478
35479         this.keyNav = new Roo.menu.MenuNav(this);
35480
35481         if(this.plain){
35482             el.addClass("x-menu-plain");
35483         }
35484         if(this.cls){
35485             el.addClass(this.cls);
35486         }
35487         // generic focus element
35488         this.focusEl = el.createChild({
35489             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35490         });
35491         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35492         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35493         
35494         ul.on("mouseover", this.onMouseOver, this);
35495         ul.on("mouseout", this.onMouseOut, this);
35496         this.items.each(function(item){
35497             if (item.hidden) {
35498                 return;
35499             }
35500             
35501             var li = document.createElement("li");
35502             li.className = "x-menu-list-item";
35503             ul.dom.appendChild(li);
35504             item.render(li, this);
35505         }, this);
35506         this.ul = ul;
35507         this.autoWidth();
35508     },
35509
35510     // private
35511     autoWidth : function(){
35512         var el = this.el, ul = this.ul;
35513         if(!el){
35514             return;
35515         }
35516         var w = this.width;
35517         if(w){
35518             el.setWidth(w);
35519         }else if(Roo.isIE){
35520             el.setWidth(this.minWidth);
35521             var t = el.dom.offsetWidth; // force recalc
35522             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35523         }
35524     },
35525
35526     // private
35527     delayAutoWidth : function(){
35528         if(this.rendered){
35529             if(!this.awTask){
35530                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35531             }
35532             this.awTask.delay(20);
35533         }
35534     },
35535
35536     // private
35537     findTargetItem : function(e){
35538         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35539         if(t && t.menuItemId){
35540             return this.items.get(t.menuItemId);
35541         }
35542     },
35543
35544     // private
35545     onClick : function(e){
35546         Roo.log("menu.onClick");
35547         var t = this.findTargetItem(e);
35548         if(!t){
35549             return;
35550         }
35551         Roo.log(e);
35552         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35553             if(t == this.activeItem && t.shouldDeactivate(e)){
35554                 this.activeItem.deactivate();
35555                 delete this.activeItem;
35556                 return;
35557             }
35558             if(t.canActivate){
35559                 this.setActiveItem(t, true);
35560             }
35561             return;
35562             
35563             
35564         }
35565         
35566         t.onClick(e);
35567         this.fireEvent("click", this, t, e);
35568     },
35569
35570     // private
35571     setActiveItem : function(item, autoExpand){
35572         if(item != this.activeItem){
35573             if(this.activeItem){
35574                 this.activeItem.deactivate();
35575             }
35576             this.activeItem = item;
35577             item.activate(autoExpand);
35578         }else if(autoExpand){
35579             item.expandMenu();
35580         }
35581     },
35582
35583     // private
35584     tryActivate : function(start, step){
35585         var items = this.items;
35586         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35587             var item = items.get(i);
35588             if(!item.disabled && item.canActivate){
35589                 this.setActiveItem(item, false);
35590                 return item;
35591             }
35592         }
35593         return false;
35594     },
35595
35596     // private
35597     onMouseOver : function(e){
35598         var t;
35599         if(t = this.findTargetItem(e)){
35600             if(t.canActivate && !t.disabled){
35601                 this.setActiveItem(t, true);
35602             }
35603         }
35604         this.fireEvent("mouseover", this, e, t);
35605     },
35606
35607     // private
35608     onMouseOut : function(e){
35609         var t;
35610         if(t = this.findTargetItem(e)){
35611             if(t == this.activeItem && t.shouldDeactivate(e)){
35612                 this.activeItem.deactivate();
35613                 delete this.activeItem;
35614             }
35615         }
35616         this.fireEvent("mouseout", this, e, t);
35617     },
35618
35619     /**
35620      * Read-only.  Returns true if the menu is currently displayed, else false.
35621      * @type Boolean
35622      */
35623     isVisible : function(){
35624         return this.el && !this.hidden;
35625     },
35626
35627     /**
35628      * Displays this menu relative to another element
35629      * @param {String/HTMLElement/Roo.Element} element The element to align to
35630      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35631      * the element (defaults to this.defaultAlign)
35632      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35633      */
35634     show : function(el, pos, parentMenu){
35635         this.parentMenu = parentMenu;
35636         if(!this.el){
35637             this.render();
35638         }
35639         this.fireEvent("beforeshow", this);
35640         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35641     },
35642
35643     /**
35644      * Displays this menu at a specific xy position
35645      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35646      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35647      */
35648     showAt : function(xy, parentMenu, /* private: */_e){
35649         this.parentMenu = parentMenu;
35650         if(!this.el){
35651             this.render();
35652         }
35653         if(_e !== false){
35654             this.fireEvent("beforeshow", this);
35655             xy = this.el.adjustForConstraints(xy);
35656         }
35657         this.el.setXY(xy);
35658         this.el.show();
35659         this.hidden = false;
35660         this.focus();
35661         this.fireEvent("show", this);
35662     },
35663
35664     focus : function(){
35665         if(!this.hidden){
35666             this.doFocus.defer(50, this);
35667         }
35668     },
35669
35670     doFocus : function(){
35671         if(!this.hidden){
35672             this.focusEl.focus();
35673         }
35674     },
35675
35676     /**
35677      * Hides this menu and optionally all parent menus
35678      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35679      */
35680     hide : function(deep){
35681         if(this.el && this.isVisible()){
35682             this.fireEvent("beforehide", this);
35683             if(this.activeItem){
35684                 this.activeItem.deactivate();
35685                 this.activeItem = null;
35686             }
35687             this.el.hide();
35688             this.hidden = true;
35689             this.fireEvent("hide", this);
35690         }
35691         if(deep === true && this.parentMenu){
35692             this.parentMenu.hide(true);
35693         }
35694     },
35695
35696     /**
35697      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35698      * Any of the following are valid:
35699      * <ul>
35700      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35701      * <li>An HTMLElement object which will be converted to a menu item</li>
35702      * <li>A menu item config object that will be created as a new menu item</li>
35703      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35704      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35705      * </ul>
35706      * Usage:
35707      * <pre><code>
35708 // Create the menu
35709 var menu = new Roo.menu.Menu();
35710
35711 // Create a menu item to add by reference
35712 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35713
35714 // Add a bunch of items at once using different methods.
35715 // Only the last item added will be returned.
35716 var item = menu.add(
35717     menuItem,                // add existing item by ref
35718     'Dynamic Item',          // new TextItem
35719     '-',                     // new separator
35720     { text: 'Config Item' }  // new item by config
35721 );
35722 </code></pre>
35723      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35724      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35725      */
35726     add : function(){
35727         var a = arguments, l = a.length, item;
35728         for(var i = 0; i < l; i++){
35729             var el = a[i];
35730             if ((typeof(el) == "object") && el.xtype && el.xns) {
35731                 el = Roo.factory(el, Roo.menu);
35732             }
35733             
35734             if(el.render){ // some kind of Item
35735                 item = this.addItem(el);
35736             }else if(typeof el == "string"){ // string
35737                 if(el == "separator" || el == "-"){
35738                     item = this.addSeparator();
35739                 }else{
35740                     item = this.addText(el);
35741                 }
35742             }else if(el.tagName || el.el){ // element
35743                 item = this.addElement(el);
35744             }else if(typeof el == "object"){ // must be menu item config?
35745                 item = this.addMenuItem(el);
35746             }
35747         }
35748         return item;
35749     },
35750
35751     /**
35752      * Returns this menu's underlying {@link Roo.Element} object
35753      * @return {Roo.Element} The element
35754      */
35755     getEl : function(){
35756         if(!this.el){
35757             this.render();
35758         }
35759         return this.el;
35760     },
35761
35762     /**
35763      * Adds a separator bar to the menu
35764      * @return {Roo.menu.Item} The menu item that was added
35765      */
35766     addSeparator : function(){
35767         return this.addItem(new Roo.menu.Separator());
35768     },
35769
35770     /**
35771      * Adds an {@link Roo.Element} object to the menu
35772      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35773      * @return {Roo.menu.Item} The menu item that was added
35774      */
35775     addElement : function(el){
35776         return this.addItem(new Roo.menu.BaseItem(el));
35777     },
35778
35779     /**
35780      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35781      * @param {Roo.menu.Item} item The menu item to add
35782      * @return {Roo.menu.Item} The menu item that was added
35783      */
35784     addItem : function(item){
35785         this.items.add(item);
35786         if(this.ul){
35787             var li = document.createElement("li");
35788             li.className = "x-menu-list-item";
35789             this.ul.dom.appendChild(li);
35790             item.render(li, this);
35791             this.delayAutoWidth();
35792         }
35793         return item;
35794     },
35795
35796     /**
35797      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35798      * @param {Object} config A MenuItem config object
35799      * @return {Roo.menu.Item} The menu item that was added
35800      */
35801     addMenuItem : function(config){
35802         if(!(config instanceof Roo.menu.Item)){
35803             if(typeof config.checked == "boolean"){ // must be check menu item config?
35804                 config = new Roo.menu.CheckItem(config);
35805             }else{
35806                 config = new Roo.menu.Item(config);
35807             }
35808         }
35809         return this.addItem(config);
35810     },
35811
35812     /**
35813      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35814      * @param {String} text The text to display in the menu item
35815      * @return {Roo.menu.Item} The menu item that was added
35816      */
35817     addText : function(text){
35818         return this.addItem(new Roo.menu.TextItem({ text : text }));
35819     },
35820
35821     /**
35822      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35823      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35824      * @param {Roo.menu.Item} item The menu item to add
35825      * @return {Roo.menu.Item} The menu item that was added
35826      */
35827     insert : function(index, item){
35828         this.items.insert(index, item);
35829         if(this.ul){
35830             var li = document.createElement("li");
35831             li.className = "x-menu-list-item";
35832             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35833             item.render(li, this);
35834             this.delayAutoWidth();
35835         }
35836         return item;
35837     },
35838
35839     /**
35840      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35841      * @param {Roo.menu.Item} item The menu item to remove
35842      */
35843     remove : function(item){
35844         this.items.removeKey(item.id);
35845         item.destroy();
35846     },
35847
35848     /**
35849      * Removes and destroys all items in the menu
35850      */
35851     removeAll : function(){
35852         var f;
35853         while(f = this.items.first()){
35854             this.remove(f);
35855         }
35856     }
35857 });
35858
35859 // MenuNav is a private utility class used internally by the Menu
35860 Roo.menu.MenuNav = function(menu){
35861     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35862     this.scope = this.menu = menu;
35863 };
35864
35865 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35866     doRelay : function(e, h){
35867         var k = e.getKey();
35868         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35869             this.menu.tryActivate(0, 1);
35870             return false;
35871         }
35872         return h.call(this.scope || this, e, this.menu);
35873     },
35874
35875     up : function(e, m){
35876         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35877             m.tryActivate(m.items.length-1, -1);
35878         }
35879     },
35880
35881     down : function(e, m){
35882         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35883             m.tryActivate(0, 1);
35884         }
35885     },
35886
35887     right : function(e, m){
35888         if(m.activeItem){
35889             m.activeItem.expandMenu(true);
35890         }
35891     },
35892
35893     left : function(e, m){
35894         m.hide();
35895         if(m.parentMenu && m.parentMenu.activeItem){
35896             m.parentMenu.activeItem.activate();
35897         }
35898     },
35899
35900     enter : function(e, m){
35901         if(m.activeItem){
35902             e.stopPropagation();
35903             m.activeItem.onClick(e);
35904             m.fireEvent("click", this, m.activeItem);
35905             return true;
35906         }
35907     }
35908 });/*
35909  * Based on:
35910  * Ext JS Library 1.1.1
35911  * Copyright(c) 2006-2007, Ext JS, LLC.
35912  *
35913  * Originally Released Under LGPL - original licence link has changed is not relivant.
35914  *
35915  * Fork - LGPL
35916  * <script type="text/javascript">
35917  */
35918  
35919 /**
35920  * @class Roo.menu.MenuMgr
35921  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35922  * @singleton
35923  */
35924 Roo.menu.MenuMgr = function(){
35925    var menus, active, groups = {}, attached = false, lastShow = new Date();
35926
35927    // private - called when first menu is created
35928    function init(){
35929        menus = {};
35930        active = new Roo.util.MixedCollection();
35931        Roo.get(document).addKeyListener(27, function(){
35932            if(active.length > 0){
35933                hideAll();
35934            }
35935        });
35936    }
35937
35938    // private
35939    function hideAll(){
35940        if(active && active.length > 0){
35941            var c = active.clone();
35942            c.each(function(m){
35943                m.hide();
35944            });
35945        }
35946    }
35947
35948    // private
35949    function onHide(m){
35950        active.remove(m);
35951        if(active.length < 1){
35952            Roo.get(document).un("mousedown", onMouseDown);
35953            attached = false;
35954        }
35955    }
35956
35957    // private
35958    function onShow(m){
35959        var last = active.last();
35960        lastShow = new Date();
35961        active.add(m);
35962        if(!attached){
35963            Roo.get(document).on("mousedown", onMouseDown);
35964            attached = true;
35965        }
35966        if(m.parentMenu){
35967           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35968           m.parentMenu.activeChild = m;
35969        }else if(last && last.isVisible()){
35970           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35971        }
35972    }
35973
35974    // private
35975    function onBeforeHide(m){
35976        if(m.activeChild){
35977            m.activeChild.hide();
35978        }
35979        if(m.autoHideTimer){
35980            clearTimeout(m.autoHideTimer);
35981            delete m.autoHideTimer;
35982        }
35983    }
35984
35985    // private
35986    function onBeforeShow(m){
35987        var pm = m.parentMenu;
35988        if(!pm && !m.allowOtherMenus){
35989            hideAll();
35990        }else if(pm && pm.activeChild && active != m){
35991            pm.activeChild.hide();
35992        }
35993    }
35994
35995    // private
35996    function onMouseDown(e){
35997        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35998            hideAll();
35999        }
36000    }
36001
36002    // private
36003    function onBeforeCheck(mi, state){
36004        if(state){
36005            var g = groups[mi.group];
36006            for(var i = 0, l = g.length; i < l; i++){
36007                if(g[i] != mi){
36008                    g[i].setChecked(false);
36009                }
36010            }
36011        }
36012    }
36013
36014    return {
36015
36016        /**
36017         * Hides all menus that are currently visible
36018         */
36019        hideAll : function(){
36020             hideAll();  
36021        },
36022
36023        // private
36024        register : function(menu){
36025            if(!menus){
36026                init();
36027            }
36028            menus[menu.id] = menu;
36029            menu.on("beforehide", onBeforeHide);
36030            menu.on("hide", onHide);
36031            menu.on("beforeshow", onBeforeShow);
36032            menu.on("show", onShow);
36033            var g = menu.group;
36034            if(g && menu.events["checkchange"]){
36035                if(!groups[g]){
36036                    groups[g] = [];
36037                }
36038                groups[g].push(menu);
36039                menu.on("checkchange", onCheck);
36040            }
36041        },
36042
36043         /**
36044          * Returns a {@link Roo.menu.Menu} object
36045          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36046          * be used to generate and return a new Menu instance.
36047          */
36048        get : function(menu){
36049            if(typeof menu == "string"){ // menu id
36050                return menus[menu];
36051            }else if(menu.events){  // menu instance
36052                return menu;
36053            }else if(typeof menu.length == 'number'){ // array of menu items?
36054                return new Roo.menu.Menu({items:menu});
36055            }else{ // otherwise, must be a config
36056                return new Roo.menu.Menu(menu);
36057            }
36058        },
36059
36060        // private
36061        unregister : function(menu){
36062            delete menus[menu.id];
36063            menu.un("beforehide", onBeforeHide);
36064            menu.un("hide", onHide);
36065            menu.un("beforeshow", onBeforeShow);
36066            menu.un("show", onShow);
36067            var g = menu.group;
36068            if(g && menu.events["checkchange"]){
36069                groups[g].remove(menu);
36070                menu.un("checkchange", onCheck);
36071            }
36072        },
36073
36074        // private
36075        registerCheckable : function(menuItem){
36076            var g = menuItem.group;
36077            if(g){
36078                if(!groups[g]){
36079                    groups[g] = [];
36080                }
36081                groups[g].push(menuItem);
36082                menuItem.on("beforecheckchange", onBeforeCheck);
36083            }
36084        },
36085
36086        // private
36087        unregisterCheckable : function(menuItem){
36088            var g = menuItem.group;
36089            if(g){
36090                groups[g].remove(menuItem);
36091                menuItem.un("beforecheckchange", onBeforeCheck);
36092            }
36093        }
36094    };
36095 }();/*
36096  * Based on:
36097  * Ext JS Library 1.1.1
36098  * Copyright(c) 2006-2007, Ext JS, LLC.
36099  *
36100  * Originally Released Under LGPL - original licence link has changed is not relivant.
36101  *
36102  * Fork - LGPL
36103  * <script type="text/javascript">
36104  */
36105  
36106
36107 /**
36108  * @class Roo.menu.BaseItem
36109  * @extends Roo.Component
36110  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36111  * management and base configuration options shared by all menu components.
36112  * @constructor
36113  * Creates a new BaseItem
36114  * @param {Object} config Configuration options
36115  */
36116 Roo.menu.BaseItem = function(config){
36117     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36118
36119     this.addEvents({
36120         /**
36121          * @event click
36122          * Fires when this item is clicked
36123          * @param {Roo.menu.BaseItem} this
36124          * @param {Roo.EventObject} e
36125          */
36126         click: true,
36127         /**
36128          * @event activate
36129          * Fires when this item is activated
36130          * @param {Roo.menu.BaseItem} this
36131          */
36132         activate : true,
36133         /**
36134          * @event deactivate
36135          * Fires when this item is deactivated
36136          * @param {Roo.menu.BaseItem} this
36137          */
36138         deactivate : true
36139     });
36140
36141     if(this.handler){
36142         this.on("click", this.handler, this.scope, true);
36143     }
36144 };
36145
36146 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36147     /**
36148      * @cfg {Function} handler
36149      * A function that will handle the click event of this menu item (defaults to undefined)
36150      */
36151     /**
36152      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36153      */
36154     canActivate : false,
36155     
36156      /**
36157      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36158      */
36159     hidden: false,
36160     
36161     /**
36162      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36163      */
36164     activeClass : "x-menu-item-active",
36165     /**
36166      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36167      */
36168     hideOnClick : true,
36169     /**
36170      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36171      */
36172     hideDelay : 100,
36173
36174     // private
36175     ctype: "Roo.menu.BaseItem",
36176
36177     // private
36178     actionMode : "container",
36179
36180     // private
36181     render : function(container, parentMenu){
36182         this.parentMenu = parentMenu;
36183         Roo.menu.BaseItem.superclass.render.call(this, container);
36184         this.container.menuItemId = this.id;
36185     },
36186
36187     // private
36188     onRender : function(container, position){
36189         this.el = Roo.get(this.el);
36190         container.dom.appendChild(this.el.dom);
36191     },
36192
36193     // private
36194     onClick : function(e){
36195         if(!this.disabled && this.fireEvent("click", this, e) !== false
36196                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36197             this.handleClick(e);
36198         }else{
36199             e.stopEvent();
36200         }
36201     },
36202
36203     // private
36204     activate : function(){
36205         if(this.disabled){
36206             return false;
36207         }
36208         var li = this.container;
36209         li.addClass(this.activeClass);
36210         this.region = li.getRegion().adjust(2, 2, -2, -2);
36211         this.fireEvent("activate", this);
36212         return true;
36213     },
36214
36215     // private
36216     deactivate : function(){
36217         this.container.removeClass(this.activeClass);
36218         this.fireEvent("deactivate", this);
36219     },
36220
36221     // private
36222     shouldDeactivate : function(e){
36223         return !this.region || !this.region.contains(e.getPoint());
36224     },
36225
36226     // private
36227     handleClick : function(e){
36228         if(this.hideOnClick){
36229             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36230         }
36231     },
36232
36233     // private
36234     expandMenu : function(autoActivate){
36235         // do nothing
36236     },
36237
36238     // private
36239     hideMenu : function(){
36240         // do nothing
36241     }
36242 });/*
36243  * Based on:
36244  * Ext JS Library 1.1.1
36245  * Copyright(c) 2006-2007, Ext JS, LLC.
36246  *
36247  * Originally Released Under LGPL - original licence link has changed is not relivant.
36248  *
36249  * Fork - LGPL
36250  * <script type="text/javascript">
36251  */
36252  
36253 /**
36254  * @class Roo.menu.Adapter
36255  * @extends Roo.menu.BaseItem
36256  * 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.
36257  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36258  * @constructor
36259  * Creates a new Adapter
36260  * @param {Object} config Configuration options
36261  */
36262 Roo.menu.Adapter = function(component, config){
36263     Roo.menu.Adapter.superclass.constructor.call(this, config);
36264     this.component = component;
36265 };
36266 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36267     // private
36268     canActivate : true,
36269
36270     // private
36271     onRender : function(container, position){
36272         this.component.render(container);
36273         this.el = this.component.getEl();
36274     },
36275
36276     // private
36277     activate : function(){
36278         if(this.disabled){
36279             return false;
36280         }
36281         this.component.focus();
36282         this.fireEvent("activate", this);
36283         return true;
36284     },
36285
36286     // private
36287     deactivate : function(){
36288         this.fireEvent("deactivate", this);
36289     },
36290
36291     // private
36292     disable : function(){
36293         this.component.disable();
36294         Roo.menu.Adapter.superclass.disable.call(this);
36295     },
36296
36297     // private
36298     enable : function(){
36299         this.component.enable();
36300         Roo.menu.Adapter.superclass.enable.call(this);
36301     }
36302 });/*
36303  * Based on:
36304  * Ext JS Library 1.1.1
36305  * Copyright(c) 2006-2007, Ext JS, LLC.
36306  *
36307  * Originally Released Under LGPL - original licence link has changed is not relivant.
36308  *
36309  * Fork - LGPL
36310  * <script type="text/javascript">
36311  */
36312
36313 /**
36314  * @class Roo.menu.TextItem
36315  * @extends Roo.menu.BaseItem
36316  * Adds a static text string to a menu, usually used as either a heading or group separator.
36317  * Note: old style constructor with text is still supported.
36318  * 
36319  * @constructor
36320  * Creates a new TextItem
36321  * @param {Object} cfg Configuration
36322  */
36323 Roo.menu.TextItem = function(cfg){
36324     if (typeof(cfg) == 'string') {
36325         this.text = cfg;
36326     } else {
36327         Roo.apply(this,cfg);
36328     }
36329     
36330     Roo.menu.TextItem.superclass.constructor.call(this);
36331 };
36332
36333 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36334     /**
36335      * @cfg {Boolean} text Text to show on item.
36336      */
36337     text : '',
36338     
36339     /**
36340      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36341      */
36342     hideOnClick : false,
36343     /**
36344      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36345      */
36346     itemCls : "x-menu-text",
36347
36348     // private
36349     onRender : function(){
36350         var s = document.createElement("span");
36351         s.className = this.itemCls;
36352         s.innerHTML = this.text;
36353         this.el = s;
36354         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36355     }
36356 });/*
36357  * Based on:
36358  * Ext JS Library 1.1.1
36359  * Copyright(c) 2006-2007, Ext JS, LLC.
36360  *
36361  * Originally Released Under LGPL - original licence link has changed is not relivant.
36362  *
36363  * Fork - LGPL
36364  * <script type="text/javascript">
36365  */
36366
36367 /**
36368  * @class Roo.menu.Separator
36369  * @extends Roo.menu.BaseItem
36370  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36371  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36372  * @constructor
36373  * @param {Object} config Configuration options
36374  */
36375 Roo.menu.Separator = function(config){
36376     Roo.menu.Separator.superclass.constructor.call(this, config);
36377 };
36378
36379 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36380     /**
36381      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36382      */
36383     itemCls : "x-menu-sep",
36384     /**
36385      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36386      */
36387     hideOnClick : false,
36388
36389     // private
36390     onRender : function(li){
36391         var s = document.createElement("span");
36392         s.className = this.itemCls;
36393         s.innerHTML = "&#160;";
36394         this.el = s;
36395         li.addClass("x-menu-sep-li");
36396         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36397     }
36398 });/*
36399  * Based on:
36400  * Ext JS Library 1.1.1
36401  * Copyright(c) 2006-2007, Ext JS, LLC.
36402  *
36403  * Originally Released Under LGPL - original licence link has changed is not relivant.
36404  *
36405  * Fork - LGPL
36406  * <script type="text/javascript">
36407  */
36408 /**
36409  * @class Roo.menu.Item
36410  * @extends Roo.menu.BaseItem
36411  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36412  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36413  * activation and click handling.
36414  * @constructor
36415  * Creates a new Item
36416  * @param {Object} config Configuration options
36417  */
36418 Roo.menu.Item = function(config){
36419     Roo.menu.Item.superclass.constructor.call(this, config);
36420     if(this.menu){
36421         this.menu = Roo.menu.MenuMgr.get(this.menu);
36422     }
36423 };
36424 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36425     
36426     /**
36427      * @cfg {String} text
36428      * The text to show on the menu item.
36429      */
36430     text: '',
36431      /**
36432      * @cfg {String} HTML to render in menu
36433      * The text to show on the menu item (HTML version).
36434      */
36435     html: '',
36436     /**
36437      * @cfg {String} icon
36438      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36439      */
36440     icon: undefined,
36441     /**
36442      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36443      */
36444     itemCls : "x-menu-item",
36445     /**
36446      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36447      */
36448     canActivate : true,
36449     /**
36450      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36451      */
36452     showDelay: 200,
36453     // doc'd in BaseItem
36454     hideDelay: 200,
36455
36456     // private
36457     ctype: "Roo.menu.Item",
36458     
36459     // private
36460     onRender : function(container, position){
36461         var el = document.createElement("a");
36462         el.hideFocus = true;
36463         el.unselectable = "on";
36464         el.href = this.href || "#";
36465         if(this.hrefTarget){
36466             el.target = this.hrefTarget;
36467         }
36468         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36469         
36470         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36471         
36472         el.innerHTML = String.format(
36473                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36474                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36475         this.el = el;
36476         Roo.menu.Item.superclass.onRender.call(this, container, position);
36477     },
36478
36479     /**
36480      * Sets the text to display in this menu item
36481      * @param {String} text The text to display
36482      * @param {Boolean} isHTML true to indicate text is pure html.
36483      */
36484     setText : function(text, isHTML){
36485         if (isHTML) {
36486             this.html = text;
36487         } else {
36488             this.text = text;
36489             this.html = '';
36490         }
36491         if(this.rendered){
36492             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36493      
36494             this.el.update(String.format(
36495                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36496                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36497             this.parentMenu.autoWidth();
36498         }
36499     },
36500
36501     // private
36502     handleClick : function(e){
36503         if(!this.href){ // if no link defined, stop the event automatically
36504             e.stopEvent();
36505         }
36506         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36507     },
36508
36509     // private
36510     activate : function(autoExpand){
36511         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36512             this.focus();
36513             if(autoExpand){
36514                 this.expandMenu();
36515             }
36516         }
36517         return true;
36518     },
36519
36520     // private
36521     shouldDeactivate : function(e){
36522         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36523             if(this.menu && this.menu.isVisible()){
36524                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36525             }
36526             return true;
36527         }
36528         return false;
36529     },
36530
36531     // private
36532     deactivate : function(){
36533         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36534         this.hideMenu();
36535     },
36536
36537     // private
36538     expandMenu : function(autoActivate){
36539         if(!this.disabled && this.menu){
36540             clearTimeout(this.hideTimer);
36541             delete this.hideTimer;
36542             if(!this.menu.isVisible() && !this.showTimer){
36543                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36544             }else if (this.menu.isVisible() && autoActivate){
36545                 this.menu.tryActivate(0, 1);
36546             }
36547         }
36548     },
36549
36550     // private
36551     deferExpand : function(autoActivate){
36552         delete this.showTimer;
36553         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36554         if(autoActivate){
36555             this.menu.tryActivate(0, 1);
36556         }
36557     },
36558
36559     // private
36560     hideMenu : function(){
36561         clearTimeout(this.showTimer);
36562         delete this.showTimer;
36563         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36564             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36565         }
36566     },
36567
36568     // private
36569     deferHide : function(){
36570         delete this.hideTimer;
36571         this.menu.hide();
36572     }
36573 });/*
36574  * Based on:
36575  * Ext JS Library 1.1.1
36576  * Copyright(c) 2006-2007, Ext JS, LLC.
36577  *
36578  * Originally Released Under LGPL - original licence link has changed is not relivant.
36579  *
36580  * Fork - LGPL
36581  * <script type="text/javascript">
36582  */
36583  
36584 /**
36585  * @class Roo.menu.CheckItem
36586  * @extends Roo.menu.Item
36587  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36588  * @constructor
36589  * Creates a new CheckItem
36590  * @param {Object} config Configuration options
36591  */
36592 Roo.menu.CheckItem = function(config){
36593     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36594     this.addEvents({
36595         /**
36596          * @event beforecheckchange
36597          * Fires before the checked value is set, providing an opportunity to cancel if needed
36598          * @param {Roo.menu.CheckItem} this
36599          * @param {Boolean} checked The new checked value that will be set
36600          */
36601         "beforecheckchange" : true,
36602         /**
36603          * @event checkchange
36604          * Fires after the checked value has been set
36605          * @param {Roo.menu.CheckItem} this
36606          * @param {Boolean} checked The checked value that was set
36607          */
36608         "checkchange" : true
36609     });
36610     if(this.checkHandler){
36611         this.on('checkchange', this.checkHandler, this.scope);
36612     }
36613 };
36614 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36615     /**
36616      * @cfg {String} group
36617      * All check items with the same group name will automatically be grouped into a single-select
36618      * radio button group (defaults to '')
36619      */
36620     /**
36621      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36622      */
36623     itemCls : "x-menu-item x-menu-check-item",
36624     /**
36625      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36626      */
36627     groupClass : "x-menu-group-item",
36628
36629     /**
36630      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36631      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36632      * initialized with checked = true will be rendered as checked.
36633      */
36634     checked: false,
36635
36636     // private
36637     ctype: "Roo.menu.CheckItem",
36638
36639     // private
36640     onRender : function(c){
36641         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36642         if(this.group){
36643             this.el.addClass(this.groupClass);
36644         }
36645         Roo.menu.MenuMgr.registerCheckable(this);
36646         if(this.checked){
36647             this.checked = false;
36648             this.setChecked(true, true);
36649         }
36650     },
36651
36652     // private
36653     destroy : function(){
36654         if(this.rendered){
36655             Roo.menu.MenuMgr.unregisterCheckable(this);
36656         }
36657         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36658     },
36659
36660     /**
36661      * Set the checked state of this item
36662      * @param {Boolean} checked The new checked value
36663      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36664      */
36665     setChecked : function(state, suppressEvent){
36666         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36667             if(this.container){
36668                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36669             }
36670             this.checked = state;
36671             if(suppressEvent !== true){
36672                 this.fireEvent("checkchange", this, state);
36673             }
36674         }
36675     },
36676
36677     // private
36678     handleClick : function(e){
36679        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36680            this.setChecked(!this.checked);
36681        }
36682        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36683     }
36684 });/*
36685  * Based on:
36686  * Ext JS Library 1.1.1
36687  * Copyright(c) 2006-2007, Ext JS, LLC.
36688  *
36689  * Originally Released Under LGPL - original licence link has changed is not relivant.
36690  *
36691  * Fork - LGPL
36692  * <script type="text/javascript">
36693  */
36694  
36695 /**
36696  * @class Roo.menu.DateItem
36697  * @extends Roo.menu.Adapter
36698  * A menu item that wraps the {@link Roo.DatPicker} component.
36699  * @constructor
36700  * Creates a new DateItem
36701  * @param {Object} config Configuration options
36702  */
36703 Roo.menu.DateItem = function(config){
36704     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36705     /** The Roo.DatePicker object @type Roo.DatePicker */
36706     this.picker = this.component;
36707     this.addEvents({select: true});
36708     
36709     this.picker.on("render", function(picker){
36710         picker.getEl().swallowEvent("click");
36711         picker.container.addClass("x-menu-date-item");
36712     });
36713
36714     this.picker.on("select", this.onSelect, this);
36715 };
36716
36717 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36718     // private
36719     onSelect : function(picker, date){
36720         this.fireEvent("select", this, date, picker);
36721         Roo.menu.DateItem.superclass.handleClick.call(this);
36722     }
36723 });/*
36724  * Based on:
36725  * Ext JS Library 1.1.1
36726  * Copyright(c) 2006-2007, Ext JS, LLC.
36727  *
36728  * Originally Released Under LGPL - original licence link has changed is not relivant.
36729  *
36730  * Fork - LGPL
36731  * <script type="text/javascript">
36732  */
36733  
36734 /**
36735  * @class Roo.menu.ColorItem
36736  * @extends Roo.menu.Adapter
36737  * A menu item that wraps the {@link Roo.ColorPalette} component.
36738  * @constructor
36739  * Creates a new ColorItem
36740  * @param {Object} config Configuration options
36741  */
36742 Roo.menu.ColorItem = function(config){
36743     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36744     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36745     this.palette = this.component;
36746     this.relayEvents(this.palette, ["select"]);
36747     if(this.selectHandler){
36748         this.on('select', this.selectHandler, this.scope);
36749     }
36750 };
36751 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36752  * Based on:
36753  * Ext JS Library 1.1.1
36754  * Copyright(c) 2006-2007, Ext JS, LLC.
36755  *
36756  * Originally Released Under LGPL - original licence link has changed is not relivant.
36757  *
36758  * Fork - LGPL
36759  * <script type="text/javascript">
36760  */
36761  
36762
36763 /**
36764  * @class Roo.menu.DateMenu
36765  * @extends Roo.menu.Menu
36766  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36767  * @constructor
36768  * Creates a new DateMenu
36769  * @param {Object} config Configuration options
36770  */
36771 Roo.menu.DateMenu = function(config){
36772     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36773     this.plain = true;
36774     var di = new Roo.menu.DateItem(config);
36775     this.add(di);
36776     /**
36777      * The {@link Roo.DatePicker} instance for this DateMenu
36778      * @type DatePicker
36779      */
36780     this.picker = di.picker;
36781     /**
36782      * @event select
36783      * @param {DatePicker} picker
36784      * @param {Date} date
36785      */
36786     this.relayEvents(di, ["select"]);
36787     this.on('beforeshow', function(){
36788         if(this.picker){
36789             this.picker.hideMonthPicker(false);
36790         }
36791     }, this);
36792 };
36793 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36794     cls:'x-date-menu'
36795 });/*
36796  * Based on:
36797  * Ext JS Library 1.1.1
36798  * Copyright(c) 2006-2007, Ext JS, LLC.
36799  *
36800  * Originally Released Under LGPL - original licence link has changed is not relivant.
36801  *
36802  * Fork - LGPL
36803  * <script type="text/javascript">
36804  */
36805  
36806
36807 /**
36808  * @class Roo.menu.ColorMenu
36809  * @extends Roo.menu.Menu
36810  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36811  * @constructor
36812  * Creates a new ColorMenu
36813  * @param {Object} config Configuration options
36814  */
36815 Roo.menu.ColorMenu = function(config){
36816     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36817     this.plain = true;
36818     var ci = new Roo.menu.ColorItem(config);
36819     this.add(ci);
36820     /**
36821      * The {@link Roo.ColorPalette} instance for this ColorMenu
36822      * @type ColorPalette
36823      */
36824     this.palette = ci.palette;
36825     /**
36826      * @event select
36827      * @param {ColorPalette} palette
36828      * @param {String} color
36829      */
36830     this.relayEvents(ci, ["select"]);
36831 };
36832 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36833  * Based on:
36834  * Ext JS Library 1.1.1
36835  * Copyright(c) 2006-2007, Ext JS, LLC.
36836  *
36837  * Originally Released Under LGPL - original licence link has changed is not relivant.
36838  *
36839  * Fork - LGPL
36840  * <script type="text/javascript">
36841  */
36842  
36843 /**
36844  * @class Roo.form.Field
36845  * @extends Roo.BoxComponent
36846  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36847  * @constructor
36848  * Creates a new Field
36849  * @param {Object} config Configuration options
36850  */
36851 Roo.form.Field = function(config){
36852     Roo.form.Field.superclass.constructor.call(this, config);
36853 };
36854
36855 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36856     /**
36857      * @cfg {String} fieldLabel Label to use when rendering a form.
36858      */
36859        /**
36860      * @cfg {String} qtip Mouse over tip
36861      */
36862      
36863     /**
36864      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36865      */
36866     invalidClass : "x-form-invalid",
36867     /**
36868      * @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")
36869      */
36870     invalidText : "The value in this field is invalid",
36871     /**
36872      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36873      */
36874     focusClass : "x-form-focus",
36875     /**
36876      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36877       automatic validation (defaults to "keyup").
36878      */
36879     validationEvent : "keyup",
36880     /**
36881      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36882      */
36883     validateOnBlur : true,
36884     /**
36885      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36886      */
36887     validationDelay : 250,
36888     /**
36889      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36890      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36891      */
36892     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36893     /**
36894      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36895      */
36896     fieldClass : "x-form-field",
36897     /**
36898      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36899      *<pre>
36900 Value         Description
36901 -----------   ----------------------------------------------------------------------
36902 qtip          Display a quick tip when the user hovers over the field
36903 title         Display a default browser title attribute popup
36904 under         Add a block div beneath the field containing the error text
36905 side          Add an error icon to the right of the field with a popup on hover
36906 [element id]  Add the error text directly to the innerHTML of the specified element
36907 </pre>
36908      */
36909     msgTarget : 'qtip',
36910     /**
36911      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36912      */
36913     msgFx : 'normal',
36914
36915     /**
36916      * @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.
36917      */
36918     readOnly : false,
36919
36920     /**
36921      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36922      */
36923     disabled : false,
36924
36925     /**
36926      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36927      */
36928     inputType : undefined,
36929     
36930     /**
36931      * @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).
36932          */
36933         tabIndex : undefined,
36934         
36935     // private
36936     isFormField : true,
36937
36938     // private
36939     hasFocus : false,
36940     /**
36941      * @property {Roo.Element} fieldEl
36942      * Element Containing the rendered Field (with label etc.)
36943      */
36944     /**
36945      * @cfg {Mixed} value A value to initialize this field with.
36946      */
36947     value : undefined,
36948
36949     /**
36950      * @cfg {String} name The field's HTML name attribute.
36951      */
36952     /**
36953      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36954      */
36955
36956         // private ??
36957         initComponent : function(){
36958         Roo.form.Field.superclass.initComponent.call(this);
36959         this.addEvents({
36960             /**
36961              * @event focus
36962              * Fires when this field receives input focus.
36963              * @param {Roo.form.Field} this
36964              */
36965             focus : true,
36966             /**
36967              * @event blur
36968              * Fires when this field loses input focus.
36969              * @param {Roo.form.Field} this
36970              */
36971             blur : true,
36972             /**
36973              * @event specialkey
36974              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36975              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36976              * @param {Roo.form.Field} this
36977              * @param {Roo.EventObject} e The event object
36978              */
36979             specialkey : true,
36980             /**
36981              * @event change
36982              * Fires just before the field blurs if the field value has changed.
36983              * @param {Roo.form.Field} this
36984              * @param {Mixed} newValue The new value
36985              * @param {Mixed} oldValue The original value
36986              */
36987             change : true,
36988             /**
36989              * @event invalid
36990              * Fires after the field has been marked as invalid.
36991              * @param {Roo.form.Field} this
36992              * @param {String} msg The validation message
36993              */
36994             invalid : true,
36995             /**
36996              * @event valid
36997              * Fires after the field has been validated with no errors.
36998              * @param {Roo.form.Field} this
36999              */
37000             valid : true,
37001              /**
37002              * @event keyup
37003              * Fires after the key up
37004              * @param {Roo.form.Field} this
37005              * @param {Roo.EventObject}  e The event Object
37006              */
37007             keyup : true
37008         });
37009     },
37010
37011     /**
37012      * Returns the name attribute of the field if available
37013      * @return {String} name The field name
37014      */
37015     getName: function(){
37016          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37017     },
37018
37019     // private
37020     onRender : function(ct, position){
37021         Roo.form.Field.superclass.onRender.call(this, ct, position);
37022         if(!this.el){
37023             var cfg = this.getAutoCreate();
37024             if(!cfg.name){
37025                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37026             }
37027             if (!cfg.name.length) {
37028                 delete cfg.name;
37029             }
37030             if(this.inputType){
37031                 cfg.type = this.inputType;
37032             }
37033             this.el = ct.createChild(cfg, position);
37034         }
37035         var type = this.el.dom.type;
37036         if(type){
37037             if(type == 'password'){
37038                 type = 'text';
37039             }
37040             this.el.addClass('x-form-'+type);
37041         }
37042         if(this.readOnly){
37043             this.el.dom.readOnly = true;
37044         }
37045         if(this.tabIndex !== undefined){
37046             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37047         }
37048
37049         this.el.addClass([this.fieldClass, this.cls]);
37050         this.initValue();
37051     },
37052
37053     /**
37054      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37055      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37056      * @return {Roo.form.Field} this
37057      */
37058     applyTo : function(target){
37059         this.allowDomMove = false;
37060         this.el = Roo.get(target);
37061         this.render(this.el.dom.parentNode);
37062         return this;
37063     },
37064
37065     // private
37066     initValue : function(){
37067         if(this.value !== undefined){
37068             this.setValue(this.value);
37069         }else if(this.el.dom.value.length > 0){
37070             this.setValue(this.el.dom.value);
37071         }
37072     },
37073
37074     /**
37075      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37076      */
37077     isDirty : function() {
37078         if(this.disabled) {
37079             return false;
37080         }
37081         return String(this.getValue()) !== String(this.originalValue);
37082     },
37083
37084     // private
37085     afterRender : function(){
37086         Roo.form.Field.superclass.afterRender.call(this);
37087         this.initEvents();
37088     },
37089
37090     // private
37091     fireKey : function(e){
37092         //Roo.log('field ' + e.getKey());
37093         if(e.isNavKeyPress()){
37094             this.fireEvent("specialkey", this, e);
37095         }
37096     },
37097
37098     /**
37099      * Resets the current field value to the originally loaded value and clears any validation messages
37100      */
37101     reset : function(){
37102         this.setValue(this.resetValue);
37103         this.clearInvalid();
37104     },
37105
37106     // private
37107     initEvents : function(){
37108         // safari killled keypress - so keydown is now used..
37109         this.el.on("keydown" , this.fireKey,  this);
37110         this.el.on("focus", this.onFocus,  this);
37111         this.el.on("blur", this.onBlur,  this);
37112         this.el.relayEvent('keyup', this);
37113
37114         // reference to original value for reset
37115         this.originalValue = this.getValue();
37116         this.resetValue =  this.getValue();
37117     },
37118
37119     // private
37120     onFocus : function(){
37121         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37122             this.el.addClass(this.focusClass);
37123         }
37124         if(!this.hasFocus){
37125             this.hasFocus = true;
37126             this.startValue = this.getValue();
37127             this.fireEvent("focus", this);
37128         }
37129     },
37130
37131     beforeBlur : Roo.emptyFn,
37132
37133     // private
37134     onBlur : function(){
37135         this.beforeBlur();
37136         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37137             this.el.removeClass(this.focusClass);
37138         }
37139         this.hasFocus = false;
37140         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37141             this.validate();
37142         }
37143         var v = this.getValue();
37144         if(String(v) !== String(this.startValue)){
37145             this.fireEvent('change', this, v, this.startValue);
37146         }
37147         this.fireEvent("blur", this);
37148     },
37149
37150     /**
37151      * Returns whether or not the field value is currently valid
37152      * @param {Boolean} preventMark True to disable marking the field invalid
37153      * @return {Boolean} True if the value is valid, else false
37154      */
37155     isValid : function(preventMark){
37156         if(this.disabled){
37157             return true;
37158         }
37159         var restore = this.preventMark;
37160         this.preventMark = preventMark === true;
37161         var v = this.validateValue(this.processValue(this.getRawValue()));
37162         this.preventMark = restore;
37163         return v;
37164     },
37165
37166     /**
37167      * Validates the field value
37168      * @return {Boolean} True if the value is valid, else false
37169      */
37170     validate : function(){
37171         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37172             this.clearInvalid();
37173             return true;
37174         }
37175         return false;
37176     },
37177
37178     processValue : function(value){
37179         return value;
37180     },
37181
37182     // private
37183     // Subclasses should provide the validation implementation by overriding this
37184     validateValue : function(value){
37185         return true;
37186     },
37187
37188     /**
37189      * Mark this field as invalid
37190      * @param {String} msg The validation message
37191      */
37192     markInvalid : function(msg){
37193         if(!this.rendered || this.preventMark){ // not rendered
37194             return;
37195         }
37196         
37197         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37198         
37199         obj.el.addClass(this.invalidClass);
37200         msg = msg || this.invalidText;
37201         switch(this.msgTarget){
37202             case 'qtip':
37203                 obj.el.dom.qtip = msg;
37204                 obj.el.dom.qclass = 'x-form-invalid-tip';
37205                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37206                     Roo.QuickTips.enable();
37207                 }
37208                 break;
37209             case 'title':
37210                 this.el.dom.title = msg;
37211                 break;
37212             case 'under':
37213                 if(!this.errorEl){
37214                     var elp = this.el.findParent('.x-form-element', 5, true);
37215                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37216                     this.errorEl.setWidth(elp.getWidth(true)-20);
37217                 }
37218                 this.errorEl.update(msg);
37219                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37220                 break;
37221             case 'side':
37222                 if(!this.errorIcon){
37223                     var elp = this.el.findParent('.x-form-element', 5, true);
37224                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37225                 }
37226                 this.alignErrorIcon();
37227                 this.errorIcon.dom.qtip = msg;
37228                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37229                 this.errorIcon.show();
37230                 this.on('resize', this.alignErrorIcon, this);
37231                 break;
37232             default:
37233                 var t = Roo.getDom(this.msgTarget);
37234                 t.innerHTML = msg;
37235                 t.style.display = this.msgDisplay;
37236                 break;
37237         }
37238         this.fireEvent('invalid', this, msg);
37239     },
37240
37241     // private
37242     alignErrorIcon : function(){
37243         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37244     },
37245
37246     /**
37247      * Clear any invalid styles/messages for this field
37248      */
37249     clearInvalid : function(){
37250         if(!this.rendered || this.preventMark){ // not rendered
37251             return;
37252         }
37253         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37254         
37255         obj.el.removeClass(this.invalidClass);
37256         switch(this.msgTarget){
37257             case 'qtip':
37258                 obj.el.dom.qtip = '';
37259                 break;
37260             case 'title':
37261                 this.el.dom.title = '';
37262                 break;
37263             case 'under':
37264                 if(this.errorEl){
37265                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37266                 }
37267                 break;
37268             case 'side':
37269                 if(this.errorIcon){
37270                     this.errorIcon.dom.qtip = '';
37271                     this.errorIcon.hide();
37272                     this.un('resize', this.alignErrorIcon, this);
37273                 }
37274                 break;
37275             default:
37276                 var t = Roo.getDom(this.msgTarget);
37277                 t.innerHTML = '';
37278                 t.style.display = 'none';
37279                 break;
37280         }
37281         this.fireEvent('valid', this);
37282     },
37283
37284     /**
37285      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37286      * @return {Mixed} value The field value
37287      */
37288     getRawValue : function(){
37289         var v = this.el.getValue();
37290         
37291         return v;
37292     },
37293
37294     /**
37295      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37296      * @return {Mixed} value The field value
37297      */
37298     getValue : function(){
37299         var v = this.el.getValue();
37300          
37301         return v;
37302     },
37303
37304     /**
37305      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37306      * @param {Mixed} value The value to set
37307      */
37308     setRawValue : function(v){
37309         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37310     },
37311
37312     /**
37313      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37314      * @param {Mixed} value The value to set
37315      */
37316     setValue : function(v){
37317         this.value = v;
37318         if(this.rendered){
37319             this.el.dom.value = (v === null || v === undefined ? '' : v);
37320              this.validate();
37321         }
37322     },
37323
37324     adjustSize : function(w, h){
37325         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37326         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37327         return s;
37328     },
37329
37330     adjustWidth : function(tag, w){
37331         tag = tag.toLowerCase();
37332         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37333             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37334                 if(tag == 'input'){
37335                     return w + 2;
37336                 }
37337                 if(tag == 'textarea'){
37338                     return w-2;
37339                 }
37340             }else if(Roo.isOpera){
37341                 if(tag == 'input'){
37342                     return w + 2;
37343                 }
37344                 if(tag == 'textarea'){
37345                     return w-2;
37346                 }
37347             }
37348         }
37349         return w;
37350     }
37351 });
37352
37353
37354 // anything other than normal should be considered experimental
37355 Roo.form.Field.msgFx = {
37356     normal : {
37357         show: function(msgEl, f){
37358             msgEl.setDisplayed('block');
37359         },
37360
37361         hide : function(msgEl, f){
37362             msgEl.setDisplayed(false).update('');
37363         }
37364     },
37365
37366     slide : {
37367         show: function(msgEl, f){
37368             msgEl.slideIn('t', {stopFx:true});
37369         },
37370
37371         hide : function(msgEl, f){
37372             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37373         }
37374     },
37375
37376     slideRight : {
37377         show: function(msgEl, f){
37378             msgEl.fixDisplay();
37379             msgEl.alignTo(f.el, 'tl-tr');
37380             msgEl.slideIn('l', {stopFx:true});
37381         },
37382
37383         hide : function(msgEl, f){
37384             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37385         }
37386     }
37387 };/*
37388  * Based on:
37389  * Ext JS Library 1.1.1
37390  * Copyright(c) 2006-2007, Ext JS, LLC.
37391  *
37392  * Originally Released Under LGPL - original licence link has changed is not relivant.
37393  *
37394  * Fork - LGPL
37395  * <script type="text/javascript">
37396  */
37397  
37398
37399 /**
37400  * @class Roo.form.TextField
37401  * @extends Roo.form.Field
37402  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37403  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37404  * @constructor
37405  * Creates a new TextField
37406  * @param {Object} config Configuration options
37407  */
37408 Roo.form.TextField = function(config){
37409     Roo.form.TextField.superclass.constructor.call(this, config);
37410     this.addEvents({
37411         /**
37412          * @event autosize
37413          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37414          * according to the default logic, but this event provides a hook for the developer to apply additional
37415          * logic at runtime to resize the field if needed.
37416              * @param {Roo.form.Field} this This text field
37417              * @param {Number} width The new field width
37418              */
37419         autosize : true
37420     });
37421 };
37422
37423 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37424     /**
37425      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37426      */
37427     grow : false,
37428     /**
37429      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37430      */
37431     growMin : 30,
37432     /**
37433      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37434      */
37435     growMax : 800,
37436     /**
37437      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37438      */
37439     vtype : null,
37440     /**
37441      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37442      */
37443     maskRe : null,
37444     /**
37445      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37446      */
37447     disableKeyFilter : false,
37448     /**
37449      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37450      */
37451     allowBlank : true,
37452     /**
37453      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37454      */
37455     minLength : 0,
37456     /**
37457      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37458      */
37459     maxLength : Number.MAX_VALUE,
37460     /**
37461      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37462      */
37463     minLengthText : "The minimum length for this field is {0}",
37464     /**
37465      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37466      */
37467     maxLengthText : "The maximum length for this field is {0}",
37468     /**
37469      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37470      */
37471     selectOnFocus : false,
37472     /**
37473      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37474      */
37475     blankText : "This field is required",
37476     /**
37477      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37478      * If available, this function will be called only after the basic validators all return true, and will be passed the
37479      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37480      */
37481     validator : null,
37482     /**
37483      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37484      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37485      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37486      */
37487     regex : null,
37488     /**
37489      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37490      */
37491     regexText : "",
37492     /**
37493      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37494      */
37495     emptyText : null,
37496    
37497
37498     // private
37499     initEvents : function()
37500     {
37501         if (this.emptyText) {
37502             this.el.attr('placeholder', this.emptyText);
37503         }
37504         
37505         Roo.form.TextField.superclass.initEvents.call(this);
37506         if(this.validationEvent == 'keyup'){
37507             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37508             this.el.on('keyup', this.filterValidation, this);
37509         }
37510         else if(this.validationEvent !== false){
37511             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37512         }
37513         
37514         if(this.selectOnFocus){
37515             this.on("focus", this.preFocus, this);
37516             
37517         }
37518         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37519             this.el.on("keypress", this.filterKeys, this);
37520         }
37521         if(this.grow){
37522             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37523             this.el.on("click", this.autoSize,  this);
37524         }
37525         if(this.el.is('input[type=password]') && Roo.isSafari){
37526             this.el.on('keydown', this.SafariOnKeyDown, this);
37527         }
37528     },
37529
37530     processValue : function(value){
37531         if(this.stripCharsRe){
37532             var newValue = value.replace(this.stripCharsRe, '');
37533             if(newValue !== value){
37534                 this.setRawValue(newValue);
37535                 return newValue;
37536             }
37537         }
37538         return value;
37539     },
37540
37541     filterValidation : function(e){
37542         if(!e.isNavKeyPress()){
37543             this.validationTask.delay(this.validationDelay);
37544         }
37545     },
37546
37547     // private
37548     onKeyUp : function(e){
37549         if(!e.isNavKeyPress()){
37550             this.autoSize();
37551         }
37552     },
37553
37554     /**
37555      * Resets the current field value to the originally-loaded value and clears any validation messages.
37556      *  
37557      */
37558     reset : function(){
37559         Roo.form.TextField.superclass.reset.call(this);
37560        
37561     },
37562
37563     
37564     // private
37565     preFocus : function(){
37566         
37567         if(this.selectOnFocus){
37568             this.el.dom.select();
37569         }
37570     },
37571
37572     
37573     // private
37574     filterKeys : function(e){
37575         var k = e.getKey();
37576         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37577             return;
37578         }
37579         var c = e.getCharCode(), cc = String.fromCharCode(c);
37580         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37581             return;
37582         }
37583         if(!this.maskRe.test(cc)){
37584             e.stopEvent();
37585         }
37586     },
37587
37588     setValue : function(v){
37589         
37590         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37591         
37592         this.autoSize();
37593     },
37594
37595     /**
37596      * Validates a value according to the field's validation rules and marks the field as invalid
37597      * if the validation fails
37598      * @param {Mixed} value The value to validate
37599      * @return {Boolean} True if the value is valid, else false
37600      */
37601     validateValue : function(value){
37602         if(value.length < 1)  { // if it's blank
37603              if(this.allowBlank){
37604                 this.clearInvalid();
37605                 return true;
37606              }else{
37607                 this.markInvalid(this.blankText);
37608                 return false;
37609              }
37610         }
37611         if(value.length < this.minLength){
37612             this.markInvalid(String.format(this.minLengthText, this.minLength));
37613             return false;
37614         }
37615         if(value.length > this.maxLength){
37616             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37617             return false;
37618         }
37619         if(this.vtype){
37620             var vt = Roo.form.VTypes;
37621             if(!vt[this.vtype](value, this)){
37622                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37623                 return false;
37624             }
37625         }
37626         if(typeof this.validator == "function"){
37627             var msg = this.validator(value);
37628             if(msg !== true){
37629                 this.markInvalid(msg);
37630                 return false;
37631             }
37632         }
37633         if(this.regex && !this.regex.test(value)){
37634             this.markInvalid(this.regexText);
37635             return false;
37636         }
37637         return true;
37638     },
37639
37640     /**
37641      * Selects text in this field
37642      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37643      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37644      */
37645     selectText : function(start, end){
37646         var v = this.getRawValue();
37647         if(v.length > 0){
37648             start = start === undefined ? 0 : start;
37649             end = end === undefined ? v.length : end;
37650             var d = this.el.dom;
37651             if(d.setSelectionRange){
37652                 d.setSelectionRange(start, end);
37653             }else if(d.createTextRange){
37654                 var range = d.createTextRange();
37655                 range.moveStart("character", start);
37656                 range.moveEnd("character", v.length-end);
37657                 range.select();
37658             }
37659         }
37660     },
37661
37662     /**
37663      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37664      * This only takes effect if grow = true, and fires the autosize event.
37665      */
37666     autoSize : function(){
37667         if(!this.grow || !this.rendered){
37668             return;
37669         }
37670         if(!this.metrics){
37671             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37672         }
37673         var el = this.el;
37674         var v = el.dom.value;
37675         var d = document.createElement('div');
37676         d.appendChild(document.createTextNode(v));
37677         v = d.innerHTML;
37678         d = null;
37679         v += "&#160;";
37680         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37681         this.el.setWidth(w);
37682         this.fireEvent("autosize", this, w);
37683     },
37684     
37685     // private
37686     SafariOnKeyDown : function(event)
37687     {
37688         // this is a workaround for a password hang bug on chrome/ webkit.
37689         
37690         var isSelectAll = false;
37691         
37692         if(this.el.dom.selectionEnd > 0){
37693             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37694         }
37695         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37696             event.preventDefault();
37697             this.setValue('');
37698             return;
37699         }
37700         
37701         if(isSelectAll){ // backspace and delete key
37702             
37703             event.preventDefault();
37704             // this is very hacky as keydown always get's upper case.
37705             //
37706             var cc = String.fromCharCode(event.getCharCode());
37707             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37708             
37709         }
37710         
37711         
37712     }
37713 });/*
37714  * Based on:
37715  * Ext JS Library 1.1.1
37716  * Copyright(c) 2006-2007, Ext JS, LLC.
37717  *
37718  * Originally Released Under LGPL - original licence link has changed is not relivant.
37719  *
37720  * Fork - LGPL
37721  * <script type="text/javascript">
37722  */
37723  
37724 /**
37725  * @class Roo.form.Hidden
37726  * @extends Roo.form.TextField
37727  * Simple Hidden element used on forms 
37728  * 
37729  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37730  * 
37731  * @constructor
37732  * Creates a new Hidden form element.
37733  * @param {Object} config Configuration options
37734  */
37735
37736
37737
37738 // easy hidden field...
37739 Roo.form.Hidden = function(config){
37740     Roo.form.Hidden.superclass.constructor.call(this, config);
37741 };
37742   
37743 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37744     fieldLabel:      '',
37745     inputType:      'hidden',
37746     width:          50,
37747     allowBlank:     true,
37748     labelSeparator: '',
37749     hidden:         true,
37750     itemCls :       'x-form-item-display-none'
37751
37752
37753 });
37754
37755
37756 /*
37757  * Based on:
37758  * Ext JS Library 1.1.1
37759  * Copyright(c) 2006-2007, Ext JS, LLC.
37760  *
37761  * Originally Released Under LGPL - original licence link has changed is not relivant.
37762  *
37763  * Fork - LGPL
37764  * <script type="text/javascript">
37765  */
37766  
37767 /**
37768  * @class Roo.form.TriggerField
37769  * @extends Roo.form.TextField
37770  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37771  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37772  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37773  * for which you can provide a custom implementation.  For example:
37774  * <pre><code>
37775 var trigger = new Roo.form.TriggerField();
37776 trigger.onTriggerClick = myTriggerFn;
37777 trigger.applyTo('my-field');
37778 </code></pre>
37779  *
37780  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37781  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37782  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37783  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37784  * @constructor
37785  * Create a new TriggerField.
37786  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37787  * to the base TextField)
37788  */
37789 Roo.form.TriggerField = function(config){
37790     this.mimicing = false;
37791     Roo.form.TriggerField.superclass.constructor.call(this, config);
37792 };
37793
37794 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37795     /**
37796      * @cfg {String} triggerClass A CSS class to apply to the trigger
37797      */
37798     /**
37799      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37800      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37801      */
37802     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37803     /**
37804      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37805      */
37806     hideTrigger:false,
37807
37808     /** @cfg {Boolean} grow @hide */
37809     /** @cfg {Number} growMin @hide */
37810     /** @cfg {Number} growMax @hide */
37811
37812     /**
37813      * @hide 
37814      * @method
37815      */
37816     autoSize: Roo.emptyFn,
37817     // private
37818     monitorTab : true,
37819     // private
37820     deferHeight : true,
37821
37822     
37823     actionMode : 'wrap',
37824     // private
37825     onResize : function(w, h){
37826         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37827         if(typeof w == 'number'){
37828             var x = w - this.trigger.getWidth();
37829             this.el.setWidth(this.adjustWidth('input', x));
37830             this.trigger.setStyle('left', x+'px');
37831         }
37832     },
37833
37834     // private
37835     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37836
37837     // private
37838     getResizeEl : function(){
37839         return this.wrap;
37840     },
37841
37842     // private
37843     getPositionEl : function(){
37844         return this.wrap;
37845     },
37846
37847     // private
37848     alignErrorIcon : function(){
37849         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37850     },
37851
37852     // private
37853     onRender : function(ct, position){
37854         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37855         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37856         this.trigger = this.wrap.createChild(this.triggerConfig ||
37857                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37858         if(this.hideTrigger){
37859             this.trigger.setDisplayed(false);
37860         }
37861         this.initTrigger();
37862         if(!this.width){
37863             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37864         }
37865     },
37866
37867     // private
37868     initTrigger : function(){
37869         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37870         this.trigger.addClassOnOver('x-form-trigger-over');
37871         this.trigger.addClassOnClick('x-form-trigger-click');
37872     },
37873
37874     // private
37875     onDestroy : function(){
37876         if(this.trigger){
37877             this.trigger.removeAllListeners();
37878             this.trigger.remove();
37879         }
37880         if(this.wrap){
37881             this.wrap.remove();
37882         }
37883         Roo.form.TriggerField.superclass.onDestroy.call(this);
37884     },
37885
37886     // private
37887     onFocus : function(){
37888         Roo.form.TriggerField.superclass.onFocus.call(this);
37889         if(!this.mimicing){
37890             this.wrap.addClass('x-trigger-wrap-focus');
37891             this.mimicing = true;
37892             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37893             if(this.monitorTab){
37894                 this.el.on("keydown", this.checkTab, this);
37895             }
37896         }
37897     },
37898
37899     // private
37900     checkTab : function(e){
37901         if(e.getKey() == e.TAB){
37902             this.triggerBlur();
37903         }
37904     },
37905
37906     // private
37907     onBlur : function(){
37908         // do nothing
37909     },
37910
37911     // private
37912     mimicBlur : function(e, t){
37913         if(!this.wrap.contains(t) && this.validateBlur()){
37914             this.triggerBlur();
37915         }
37916     },
37917
37918     // private
37919     triggerBlur : function(){
37920         this.mimicing = false;
37921         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37922         if(this.monitorTab){
37923             this.el.un("keydown", this.checkTab, this);
37924         }
37925         this.wrap.removeClass('x-trigger-wrap-focus');
37926         Roo.form.TriggerField.superclass.onBlur.call(this);
37927     },
37928
37929     // private
37930     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37931     validateBlur : function(e, t){
37932         return true;
37933     },
37934
37935     // private
37936     onDisable : function(){
37937         Roo.form.TriggerField.superclass.onDisable.call(this);
37938         if(this.wrap){
37939             this.wrap.addClass('x-item-disabled');
37940         }
37941     },
37942
37943     // private
37944     onEnable : function(){
37945         Roo.form.TriggerField.superclass.onEnable.call(this);
37946         if(this.wrap){
37947             this.wrap.removeClass('x-item-disabled');
37948         }
37949     },
37950
37951     // private
37952     onShow : function(){
37953         var ae = this.getActionEl();
37954         
37955         if(ae){
37956             ae.dom.style.display = '';
37957             ae.dom.style.visibility = 'visible';
37958         }
37959     },
37960
37961     // private
37962     
37963     onHide : function(){
37964         var ae = this.getActionEl();
37965         ae.dom.style.display = 'none';
37966     },
37967
37968     /**
37969      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37970      * by an implementing function.
37971      * @method
37972      * @param {EventObject} e
37973      */
37974     onTriggerClick : Roo.emptyFn
37975 });
37976
37977 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37978 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37979 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37980 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37981     initComponent : function(){
37982         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37983
37984         this.triggerConfig = {
37985             tag:'span', cls:'x-form-twin-triggers', cn:[
37986             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37987             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37988         ]};
37989     },
37990
37991     getTrigger : function(index){
37992         return this.triggers[index];
37993     },
37994
37995     initTrigger : function(){
37996         var ts = this.trigger.select('.x-form-trigger', true);
37997         this.wrap.setStyle('overflow', 'hidden');
37998         var triggerField = this;
37999         ts.each(function(t, all, index){
38000             t.hide = function(){
38001                 var w = triggerField.wrap.getWidth();
38002                 this.dom.style.display = 'none';
38003                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38004             };
38005             t.show = function(){
38006                 var w = triggerField.wrap.getWidth();
38007                 this.dom.style.display = '';
38008                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38009             };
38010             var triggerIndex = 'Trigger'+(index+1);
38011
38012             if(this['hide'+triggerIndex]){
38013                 t.dom.style.display = 'none';
38014             }
38015             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38016             t.addClassOnOver('x-form-trigger-over');
38017             t.addClassOnClick('x-form-trigger-click');
38018         }, this);
38019         this.triggers = ts.elements;
38020     },
38021
38022     onTrigger1Click : Roo.emptyFn,
38023     onTrigger2Click : Roo.emptyFn
38024 });/*
38025  * Based on:
38026  * Ext JS Library 1.1.1
38027  * Copyright(c) 2006-2007, Ext JS, LLC.
38028  *
38029  * Originally Released Under LGPL - original licence link has changed is not relivant.
38030  *
38031  * Fork - LGPL
38032  * <script type="text/javascript">
38033  */
38034  
38035 /**
38036  * @class Roo.form.TextArea
38037  * @extends Roo.form.TextField
38038  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38039  * support for auto-sizing.
38040  * @constructor
38041  * Creates a new TextArea
38042  * @param {Object} config Configuration options
38043  */
38044 Roo.form.TextArea = function(config){
38045     Roo.form.TextArea.superclass.constructor.call(this, config);
38046     // these are provided exchanges for backwards compat
38047     // minHeight/maxHeight were replaced by growMin/growMax to be
38048     // compatible with TextField growing config values
38049     if(this.minHeight !== undefined){
38050         this.growMin = this.minHeight;
38051     }
38052     if(this.maxHeight !== undefined){
38053         this.growMax = this.maxHeight;
38054     }
38055 };
38056
38057 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38058     /**
38059      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38060      */
38061     growMin : 60,
38062     /**
38063      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38064      */
38065     growMax: 1000,
38066     /**
38067      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38068      * in the field (equivalent to setting overflow: hidden, defaults to false)
38069      */
38070     preventScrollbars: false,
38071     /**
38072      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38073      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38074      */
38075
38076     // private
38077     onRender : function(ct, position){
38078         if(!this.el){
38079             this.defaultAutoCreate = {
38080                 tag: "textarea",
38081                 style:"width:300px;height:60px;",
38082                 autocomplete: "off"
38083             };
38084         }
38085         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38086         if(this.grow){
38087             this.textSizeEl = Roo.DomHelper.append(document.body, {
38088                 tag: "pre", cls: "x-form-grow-sizer"
38089             });
38090             if(this.preventScrollbars){
38091                 this.el.setStyle("overflow", "hidden");
38092             }
38093             this.el.setHeight(this.growMin);
38094         }
38095     },
38096
38097     onDestroy : function(){
38098         if(this.textSizeEl){
38099             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38100         }
38101         Roo.form.TextArea.superclass.onDestroy.call(this);
38102     },
38103
38104     // private
38105     onKeyUp : function(e){
38106         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38107             this.autoSize();
38108         }
38109     },
38110
38111     /**
38112      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38113      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38114      */
38115     autoSize : function(){
38116         if(!this.grow || !this.textSizeEl){
38117             return;
38118         }
38119         var el = this.el;
38120         var v = el.dom.value;
38121         var ts = this.textSizeEl;
38122
38123         ts.innerHTML = '';
38124         ts.appendChild(document.createTextNode(v));
38125         v = ts.innerHTML;
38126
38127         Roo.fly(ts).setWidth(this.el.getWidth());
38128         if(v.length < 1){
38129             v = "&#160;&#160;";
38130         }else{
38131             if(Roo.isIE){
38132                 v = v.replace(/\n/g, '<p>&#160;</p>');
38133             }
38134             v += "&#160;\n&#160;";
38135         }
38136         ts.innerHTML = v;
38137         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38138         if(h != this.lastHeight){
38139             this.lastHeight = h;
38140             this.el.setHeight(h);
38141             this.fireEvent("autosize", this, h);
38142         }
38143     }
38144 });/*
38145  * Based on:
38146  * Ext JS Library 1.1.1
38147  * Copyright(c) 2006-2007, Ext JS, LLC.
38148  *
38149  * Originally Released Under LGPL - original licence link has changed is not relivant.
38150  *
38151  * Fork - LGPL
38152  * <script type="text/javascript">
38153  */
38154  
38155
38156 /**
38157  * @class Roo.form.NumberField
38158  * @extends Roo.form.TextField
38159  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38160  * @constructor
38161  * Creates a new NumberField
38162  * @param {Object} config Configuration options
38163  */
38164 Roo.form.NumberField = function(config){
38165     Roo.form.NumberField.superclass.constructor.call(this, config);
38166 };
38167
38168 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38169     /**
38170      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38171      */
38172     fieldClass: "x-form-field x-form-num-field",
38173     /**
38174      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38175      */
38176     allowDecimals : true,
38177     /**
38178      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38179      */
38180     decimalSeparator : ".",
38181     /**
38182      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38183      */
38184     decimalPrecision : 2,
38185     /**
38186      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38187      */
38188     allowNegative : true,
38189     /**
38190      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38191      */
38192     minValue : Number.NEGATIVE_INFINITY,
38193     /**
38194      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38195      */
38196     maxValue : Number.MAX_VALUE,
38197     /**
38198      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38199      */
38200     minText : "The minimum value for this field is {0}",
38201     /**
38202      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38203      */
38204     maxText : "The maximum value for this field is {0}",
38205     /**
38206      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38207      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38208      */
38209     nanText : "{0} is not a valid number",
38210
38211     // private
38212     initEvents : function(){
38213         Roo.form.NumberField.superclass.initEvents.call(this);
38214         var allowed = "0123456789";
38215         if(this.allowDecimals){
38216             allowed += this.decimalSeparator;
38217         }
38218         if(this.allowNegative){
38219             allowed += "-";
38220         }
38221         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38222         var keyPress = function(e){
38223             var k = e.getKey();
38224             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38225                 return;
38226             }
38227             var c = e.getCharCode();
38228             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38229                 e.stopEvent();
38230             }
38231         };
38232         this.el.on("keypress", keyPress, this);
38233     },
38234
38235     // private
38236     validateValue : function(value){
38237         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38238             return false;
38239         }
38240         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38241              return true;
38242         }
38243         var num = this.parseValue(value);
38244         if(isNaN(num)){
38245             this.markInvalid(String.format(this.nanText, value));
38246             return false;
38247         }
38248         if(num < this.minValue){
38249             this.markInvalid(String.format(this.minText, this.minValue));
38250             return false;
38251         }
38252         if(num > this.maxValue){
38253             this.markInvalid(String.format(this.maxText, this.maxValue));
38254             return false;
38255         }
38256         return true;
38257     },
38258
38259     getValue : function(){
38260         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38261     },
38262
38263     // private
38264     parseValue : function(value){
38265         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38266         return isNaN(value) ? '' : value;
38267     },
38268
38269     // private
38270     fixPrecision : function(value){
38271         var nan = isNaN(value);
38272         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38273             return nan ? '' : value;
38274         }
38275         return parseFloat(value).toFixed(this.decimalPrecision);
38276     },
38277
38278     setValue : function(v){
38279         v = this.fixPrecision(v);
38280         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38281     },
38282
38283     // private
38284     decimalPrecisionFcn : function(v){
38285         return Math.floor(v);
38286     },
38287
38288     beforeBlur : function(){
38289         var v = this.parseValue(this.getRawValue());
38290         if(v){
38291             this.setValue(v);
38292         }
38293     }
38294 });/*
38295  * Based on:
38296  * Ext JS Library 1.1.1
38297  * Copyright(c) 2006-2007, Ext JS, LLC.
38298  *
38299  * Originally Released Under LGPL - original licence link has changed is not relivant.
38300  *
38301  * Fork - LGPL
38302  * <script type="text/javascript">
38303  */
38304  
38305 /**
38306  * @class Roo.form.DateField
38307  * @extends Roo.form.TriggerField
38308  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38309 * @constructor
38310 * Create a new DateField
38311 * @param {Object} config
38312  */
38313 Roo.form.DateField = function(config){
38314     Roo.form.DateField.superclass.constructor.call(this, config);
38315     
38316       this.addEvents({
38317          
38318         /**
38319          * @event select
38320          * Fires when a date is selected
38321              * @param {Roo.form.DateField} combo This combo box
38322              * @param {Date} date The date selected
38323              */
38324         'select' : true
38325          
38326     });
38327     
38328     
38329     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38330     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38331     this.ddMatch = null;
38332     if(this.disabledDates){
38333         var dd = this.disabledDates;
38334         var re = "(?:";
38335         for(var i = 0; i < dd.length; i++){
38336             re += dd[i];
38337             if(i != dd.length-1) re += "|";
38338         }
38339         this.ddMatch = new RegExp(re + ")");
38340     }
38341 };
38342
38343 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38344     /**
38345      * @cfg {String} format
38346      * The default date format string which can be overriden for localization support.  The format must be
38347      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38348      */
38349     format : "m/d/y",
38350     /**
38351      * @cfg {String} altFormats
38352      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38353      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38354      */
38355     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38356     /**
38357      * @cfg {Array} disabledDays
38358      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38359      */
38360     disabledDays : null,
38361     /**
38362      * @cfg {String} disabledDaysText
38363      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38364      */
38365     disabledDaysText : "Disabled",
38366     /**
38367      * @cfg {Array} disabledDates
38368      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38369      * expression so they are very powerful. Some examples:
38370      * <ul>
38371      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38372      * <li>["03/08", "09/16"] would disable those days for every year</li>
38373      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38374      * <li>["03/../2006"] would disable every day in March 2006</li>
38375      * <li>["^03"] would disable every day in every March</li>
38376      * </ul>
38377      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38378      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38379      */
38380     disabledDates : null,
38381     /**
38382      * @cfg {String} disabledDatesText
38383      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38384      */
38385     disabledDatesText : "Disabled",
38386     /**
38387      * @cfg {Date/String} minValue
38388      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38389      * valid format (defaults to null).
38390      */
38391     minValue : null,
38392     /**
38393      * @cfg {Date/String} maxValue
38394      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38395      * valid format (defaults to null).
38396      */
38397     maxValue : null,
38398     /**
38399      * @cfg {String} minText
38400      * The error text to display when the date in the cell is before minValue (defaults to
38401      * 'The date in this field must be after {minValue}').
38402      */
38403     minText : "The date in this field must be equal to or after {0}",
38404     /**
38405      * @cfg {String} maxText
38406      * The error text to display when the date in the cell is after maxValue (defaults to
38407      * 'The date in this field must be before {maxValue}').
38408      */
38409     maxText : "The date in this field must be equal to or before {0}",
38410     /**
38411      * @cfg {String} invalidText
38412      * The error text to display when the date in the field is invalid (defaults to
38413      * '{value} is not a valid date - it must be in the format {format}').
38414      */
38415     invalidText : "{0} is not a valid date - it must be in the format {1}",
38416     /**
38417      * @cfg {String} triggerClass
38418      * An additional CSS class used to style the trigger button.  The trigger will always get the
38419      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38420      * which displays a calendar icon).
38421      */
38422     triggerClass : 'x-form-date-trigger',
38423     
38424
38425     /**
38426      * @cfg {Boolean} useIso
38427      * if enabled, then the date field will use a hidden field to store the 
38428      * real value as iso formated date. default (false)
38429      */ 
38430     useIso : false,
38431     /**
38432      * @cfg {String/Object} autoCreate
38433      * A DomHelper element spec, or true for a default element spec (defaults to
38434      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38435      */ 
38436     // private
38437     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38438     
38439     // private
38440     hiddenField: false,
38441     
38442     onRender : function(ct, position)
38443     {
38444         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38445         if (this.useIso) {
38446             //this.el.dom.removeAttribute('name'); 
38447             Roo.log("Changing name?");
38448             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38449             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38450                     'before', true);
38451             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38452             // prevent input submission
38453             this.hiddenName = this.name;
38454         }
38455             
38456             
38457     },
38458     
38459     // private
38460     validateValue : function(value)
38461     {
38462         value = this.formatDate(value);
38463         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38464             Roo.log('super failed');
38465             return false;
38466         }
38467         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38468              return true;
38469         }
38470         var svalue = value;
38471         value = this.parseDate(value);
38472         if(!value){
38473             Roo.log('parse date failed' + svalue);
38474             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38475             return false;
38476         }
38477         var time = value.getTime();
38478         if(this.minValue && time < this.minValue.getTime()){
38479             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38480             return false;
38481         }
38482         if(this.maxValue && time > this.maxValue.getTime()){
38483             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38484             return false;
38485         }
38486         if(this.disabledDays){
38487             var day = value.getDay();
38488             for(var i = 0; i < this.disabledDays.length; i++) {
38489                 if(day === this.disabledDays[i]){
38490                     this.markInvalid(this.disabledDaysText);
38491                     return false;
38492                 }
38493             }
38494         }
38495         var fvalue = this.formatDate(value);
38496         if(this.ddMatch && this.ddMatch.test(fvalue)){
38497             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38498             return false;
38499         }
38500         return true;
38501     },
38502
38503     // private
38504     // Provides logic to override the default TriggerField.validateBlur which just returns true
38505     validateBlur : function(){
38506         return !this.menu || !this.menu.isVisible();
38507     },
38508     
38509     getName: function()
38510     {
38511         // returns hidden if it's set..
38512         if (!this.rendered) {return ''};
38513         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38514         
38515     },
38516
38517     /**
38518      * Returns the current date value of the date field.
38519      * @return {Date} The date value
38520      */
38521     getValue : function(){
38522         
38523         return  this.hiddenField ?
38524                 this.hiddenField.value :
38525                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38526     },
38527
38528     /**
38529      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38530      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38531      * (the default format used is "m/d/y").
38532      * <br />Usage:
38533      * <pre><code>
38534 //All of these calls set the same date value (May 4, 2006)
38535
38536 //Pass a date object:
38537 var dt = new Date('5/4/06');
38538 dateField.setValue(dt);
38539
38540 //Pass a date string (default format):
38541 dateField.setValue('5/4/06');
38542
38543 //Pass a date string (custom format):
38544 dateField.format = 'Y-m-d';
38545 dateField.setValue('2006-5-4');
38546 </code></pre>
38547      * @param {String/Date} date The date or valid date string
38548      */
38549     setValue : function(date){
38550         if (this.hiddenField) {
38551             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38552         }
38553         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38554         // make sure the value field is always stored as a date..
38555         this.value = this.parseDate(date);
38556         
38557         
38558     },
38559
38560     // private
38561     parseDate : function(value){
38562         if(!value || value instanceof Date){
38563             return value;
38564         }
38565         var v = Date.parseDate(value, this.format);
38566          if (!v && this.useIso) {
38567             v = Date.parseDate(value, 'Y-m-d');
38568         }
38569         if(!v && this.altFormats){
38570             if(!this.altFormatsArray){
38571                 this.altFormatsArray = this.altFormats.split("|");
38572             }
38573             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38574                 v = Date.parseDate(value, this.altFormatsArray[i]);
38575             }
38576         }
38577         return v;
38578     },
38579
38580     // private
38581     formatDate : function(date, fmt){
38582         return (!date || !(date instanceof Date)) ?
38583                date : date.dateFormat(fmt || this.format);
38584     },
38585
38586     // private
38587     menuListeners : {
38588         select: function(m, d){
38589             
38590             this.setValue(d);
38591             this.fireEvent('select', this, d);
38592         },
38593         show : function(){ // retain focus styling
38594             this.onFocus();
38595         },
38596         hide : function(){
38597             this.focus.defer(10, this);
38598             var ml = this.menuListeners;
38599             this.menu.un("select", ml.select,  this);
38600             this.menu.un("show", ml.show,  this);
38601             this.menu.un("hide", ml.hide,  this);
38602         }
38603     },
38604
38605     // private
38606     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38607     onTriggerClick : function(){
38608         if(this.disabled){
38609             return;
38610         }
38611         if(this.menu == null){
38612             this.menu = new Roo.menu.DateMenu();
38613         }
38614         Roo.apply(this.menu.picker,  {
38615             showClear: this.allowBlank,
38616             minDate : this.minValue,
38617             maxDate : this.maxValue,
38618             disabledDatesRE : this.ddMatch,
38619             disabledDatesText : this.disabledDatesText,
38620             disabledDays : this.disabledDays,
38621             disabledDaysText : this.disabledDaysText,
38622             format : this.useIso ? 'Y-m-d' : this.format,
38623             minText : String.format(this.minText, this.formatDate(this.minValue)),
38624             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38625         });
38626         this.menu.on(Roo.apply({}, this.menuListeners, {
38627             scope:this
38628         }));
38629         this.menu.picker.setValue(this.getValue() || new Date());
38630         this.menu.show(this.el, "tl-bl?");
38631     },
38632
38633     beforeBlur : function(){
38634         var v = this.parseDate(this.getRawValue());
38635         if(v){
38636             this.setValue(v);
38637         }
38638     },
38639
38640     /*@
38641      * overide
38642      * 
38643      */
38644     isDirty : function() {
38645         if(this.disabled) {
38646             return false;
38647         }
38648         
38649         if(typeof(this.startValue) === 'undefined'){
38650             return false;
38651         }
38652         
38653         return String(this.getValue()) !== String(this.startValue);
38654         
38655     }
38656 });/*
38657  * Based on:
38658  * Ext JS Library 1.1.1
38659  * Copyright(c) 2006-2007, Ext JS, LLC.
38660  *
38661  * Originally Released Under LGPL - original licence link has changed is not relivant.
38662  *
38663  * Fork - LGPL
38664  * <script type="text/javascript">
38665  */
38666  
38667 /**
38668  * @class Roo.form.MonthField
38669  * @extends Roo.form.TriggerField
38670  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38671 * @constructor
38672 * Create a new MonthField
38673 * @param {Object} config
38674  */
38675 Roo.form.MonthField = function(config){
38676     
38677     Roo.form.MonthField.superclass.constructor.call(this, config);
38678     
38679       this.addEvents({
38680          
38681         /**
38682          * @event select
38683          * Fires when a date is selected
38684              * @param {Roo.form.MonthFieeld} combo This combo box
38685              * @param {Date} date The date selected
38686              */
38687         'select' : true
38688          
38689     });
38690     
38691     
38692     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38693     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38694     this.ddMatch = null;
38695     if(this.disabledDates){
38696         var dd = this.disabledDates;
38697         var re = "(?:";
38698         for(var i = 0; i < dd.length; i++){
38699             re += dd[i];
38700             if(i != dd.length-1) re += "|";
38701         }
38702         this.ddMatch = new RegExp(re + ")");
38703     }
38704 };
38705
38706 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38707     /**
38708      * @cfg {String} format
38709      * The default date format string which can be overriden for localization support.  The format must be
38710      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38711      */
38712     format : "M Y",
38713     /**
38714      * @cfg {String} altFormats
38715      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38716      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38717      */
38718     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38719     /**
38720      * @cfg {Array} disabledDays
38721      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38722      */
38723     disabledDays : [0,1,2,3,4,5,6],
38724     /**
38725      * @cfg {String} disabledDaysText
38726      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38727      */
38728     disabledDaysText : "Disabled",
38729     /**
38730      * @cfg {Array} disabledDates
38731      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38732      * expression so they are very powerful. Some examples:
38733      * <ul>
38734      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38735      * <li>["03/08", "09/16"] would disable those days for every year</li>
38736      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38737      * <li>["03/../2006"] would disable every day in March 2006</li>
38738      * <li>["^03"] would disable every day in every March</li>
38739      * </ul>
38740      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38741      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38742      */
38743     disabledDates : null,
38744     /**
38745      * @cfg {String} disabledDatesText
38746      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38747      */
38748     disabledDatesText : "Disabled",
38749     /**
38750      * @cfg {Date/String} minValue
38751      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38752      * valid format (defaults to null).
38753      */
38754     minValue : null,
38755     /**
38756      * @cfg {Date/String} maxValue
38757      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38758      * valid format (defaults to null).
38759      */
38760     maxValue : null,
38761     /**
38762      * @cfg {String} minText
38763      * The error text to display when the date in the cell is before minValue (defaults to
38764      * 'The date in this field must be after {minValue}').
38765      */
38766     minText : "The date in this field must be equal to or after {0}",
38767     /**
38768      * @cfg {String} maxTextf
38769      * The error text to display when the date in the cell is after maxValue (defaults to
38770      * 'The date in this field must be before {maxValue}').
38771      */
38772     maxText : "The date in this field must be equal to or before {0}",
38773     /**
38774      * @cfg {String} invalidText
38775      * The error text to display when the date in the field is invalid (defaults to
38776      * '{value} is not a valid date - it must be in the format {format}').
38777      */
38778     invalidText : "{0} is not a valid date - it must be in the format {1}",
38779     /**
38780      * @cfg {String} triggerClass
38781      * An additional CSS class used to style the trigger button.  The trigger will always get the
38782      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38783      * which displays a calendar icon).
38784      */
38785     triggerClass : 'x-form-date-trigger',
38786     
38787
38788     /**
38789      * @cfg {Boolean} useIso
38790      * if enabled, then the date field will use a hidden field to store the 
38791      * real value as iso formated date. default (true)
38792      */ 
38793     useIso : true,
38794     /**
38795      * @cfg {String/Object} autoCreate
38796      * A DomHelper element spec, or true for a default element spec (defaults to
38797      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38798      */ 
38799     // private
38800     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38801     
38802     // private
38803     hiddenField: false,
38804     
38805     hideMonthPicker : false,
38806     
38807     onRender : function(ct, position)
38808     {
38809         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38810         if (this.useIso) {
38811             this.el.dom.removeAttribute('name'); 
38812             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38813                     'before', true);
38814             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38815             // prevent input submission
38816             this.hiddenName = this.name;
38817         }
38818             
38819             
38820     },
38821     
38822     // private
38823     validateValue : function(value)
38824     {
38825         value = this.formatDate(value);
38826         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38827             return false;
38828         }
38829         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38830              return true;
38831         }
38832         var svalue = value;
38833         value = this.parseDate(value);
38834         if(!value){
38835             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38836             return false;
38837         }
38838         var time = value.getTime();
38839         if(this.minValue && time < this.minValue.getTime()){
38840             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38841             return false;
38842         }
38843         if(this.maxValue && time > this.maxValue.getTime()){
38844             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38845             return false;
38846         }
38847         /*if(this.disabledDays){
38848             var day = value.getDay();
38849             for(var i = 0; i < this.disabledDays.length; i++) {
38850                 if(day === this.disabledDays[i]){
38851                     this.markInvalid(this.disabledDaysText);
38852                     return false;
38853                 }
38854             }
38855         }
38856         */
38857         var fvalue = this.formatDate(value);
38858         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38859             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38860             return false;
38861         }
38862         */
38863         return true;
38864     },
38865
38866     // private
38867     // Provides logic to override the default TriggerField.validateBlur which just returns true
38868     validateBlur : function(){
38869         return !this.menu || !this.menu.isVisible();
38870     },
38871
38872     /**
38873      * Returns the current date value of the date field.
38874      * @return {Date} The date value
38875      */
38876     getValue : function(){
38877         
38878         
38879         
38880         return  this.hiddenField ?
38881                 this.hiddenField.value :
38882                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38883     },
38884
38885     /**
38886      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38887      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38888      * (the default format used is "m/d/y").
38889      * <br />Usage:
38890      * <pre><code>
38891 //All of these calls set the same date value (May 4, 2006)
38892
38893 //Pass a date object:
38894 var dt = new Date('5/4/06');
38895 monthField.setValue(dt);
38896
38897 //Pass a date string (default format):
38898 monthField.setValue('5/4/06');
38899
38900 //Pass a date string (custom format):
38901 monthField.format = 'Y-m-d';
38902 monthField.setValue('2006-5-4');
38903 </code></pre>
38904      * @param {String/Date} date The date or valid date string
38905      */
38906     setValue : function(date){
38907         Roo.log('month setValue' + date);
38908         // can only be first of month..
38909         
38910         var val = this.parseDate(date);
38911         
38912         if (this.hiddenField) {
38913             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38914         }
38915         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38916         this.value = this.parseDate(date);
38917     },
38918
38919     // private
38920     parseDate : function(value){
38921         if(!value || value instanceof Date){
38922             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38923             return value;
38924         }
38925         var v = Date.parseDate(value, this.format);
38926         if (!v && this.useIso) {
38927             v = Date.parseDate(value, 'Y-m-d');
38928         }
38929         if (v) {
38930             // 
38931             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38932         }
38933         
38934         
38935         if(!v && this.altFormats){
38936             if(!this.altFormatsArray){
38937                 this.altFormatsArray = this.altFormats.split("|");
38938             }
38939             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38940                 v = Date.parseDate(value, this.altFormatsArray[i]);
38941             }
38942         }
38943         return v;
38944     },
38945
38946     // private
38947     formatDate : function(date, fmt){
38948         return (!date || !(date instanceof Date)) ?
38949                date : date.dateFormat(fmt || this.format);
38950     },
38951
38952     // private
38953     menuListeners : {
38954         select: function(m, d){
38955             this.setValue(d);
38956             this.fireEvent('select', this, d);
38957         },
38958         show : function(){ // retain focus styling
38959             this.onFocus();
38960         },
38961         hide : function(){
38962             this.focus.defer(10, this);
38963             var ml = this.menuListeners;
38964             this.menu.un("select", ml.select,  this);
38965             this.menu.un("show", ml.show,  this);
38966             this.menu.un("hide", ml.hide,  this);
38967         }
38968     },
38969     // private
38970     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38971     onTriggerClick : function(){
38972         if(this.disabled){
38973             return;
38974         }
38975         if(this.menu == null){
38976             this.menu = new Roo.menu.DateMenu();
38977            
38978         }
38979         
38980         Roo.apply(this.menu.picker,  {
38981             
38982             showClear: this.allowBlank,
38983             minDate : this.minValue,
38984             maxDate : this.maxValue,
38985             disabledDatesRE : this.ddMatch,
38986             disabledDatesText : this.disabledDatesText,
38987             
38988             format : this.useIso ? 'Y-m-d' : this.format,
38989             minText : String.format(this.minText, this.formatDate(this.minValue)),
38990             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38991             
38992         });
38993          this.menu.on(Roo.apply({}, this.menuListeners, {
38994             scope:this
38995         }));
38996        
38997         
38998         var m = this.menu;
38999         var p = m.picker;
39000         
39001         // hide month picker get's called when we called by 'before hide';
39002         
39003         var ignorehide = true;
39004         p.hideMonthPicker  = function(disableAnim){
39005             if (ignorehide) {
39006                 return;
39007             }
39008              if(this.monthPicker){
39009                 Roo.log("hideMonthPicker called");
39010                 if(disableAnim === true){
39011                     this.monthPicker.hide();
39012                 }else{
39013                     this.monthPicker.slideOut('t', {duration:.2});
39014                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39015                     p.fireEvent("select", this, this.value);
39016                     m.hide();
39017                 }
39018             }
39019         }
39020         
39021         Roo.log('picker set value');
39022         Roo.log(this.getValue());
39023         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39024         m.show(this.el, 'tl-bl?');
39025         ignorehide  = false;
39026         // this will trigger hideMonthPicker..
39027         
39028         
39029         // hidden the day picker
39030         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39031         
39032         
39033         
39034       
39035         
39036         p.showMonthPicker.defer(100, p);
39037     
39038         
39039        
39040     },
39041
39042     beforeBlur : function(){
39043         var v = this.parseDate(this.getRawValue());
39044         if(v){
39045             this.setValue(v);
39046         }
39047     }
39048
39049     /** @cfg {Boolean} grow @hide */
39050     /** @cfg {Number} growMin @hide */
39051     /** @cfg {Number} growMax @hide */
39052     /**
39053      * @hide
39054      * @method autoSize
39055      */
39056 });/*
39057  * Based on:
39058  * Ext JS Library 1.1.1
39059  * Copyright(c) 2006-2007, Ext JS, LLC.
39060  *
39061  * Originally Released Under LGPL - original licence link has changed is not relivant.
39062  *
39063  * Fork - LGPL
39064  * <script type="text/javascript">
39065  */
39066  
39067
39068 /**
39069  * @class Roo.form.ComboBox
39070  * @extends Roo.form.TriggerField
39071  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39072  * @constructor
39073  * Create a new ComboBox.
39074  * @param {Object} config Configuration options
39075  */
39076 Roo.form.ComboBox = function(config){
39077     Roo.form.ComboBox.superclass.constructor.call(this, config);
39078     this.addEvents({
39079         /**
39080          * @event expand
39081          * Fires when the dropdown list is expanded
39082              * @param {Roo.form.ComboBox} combo This combo box
39083              */
39084         'expand' : true,
39085         /**
39086          * @event collapse
39087          * Fires when the dropdown list is collapsed
39088              * @param {Roo.form.ComboBox} combo This combo box
39089              */
39090         'collapse' : true,
39091         /**
39092          * @event beforeselect
39093          * Fires before a list item is selected. Return false to cancel the selection.
39094              * @param {Roo.form.ComboBox} combo This combo box
39095              * @param {Roo.data.Record} record The data record returned from the underlying store
39096              * @param {Number} index The index of the selected item in the dropdown list
39097              */
39098         'beforeselect' : true,
39099         /**
39100          * @event select
39101          * Fires when a list item is selected
39102              * @param {Roo.form.ComboBox} combo This combo box
39103              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39104              * @param {Number} index The index of the selected item in the dropdown list
39105              */
39106         'select' : true,
39107         /**
39108          * @event beforequery
39109          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39110          * The event object passed has these properties:
39111              * @param {Roo.form.ComboBox} combo This combo box
39112              * @param {String} query The query
39113              * @param {Boolean} forceAll true to force "all" query
39114              * @param {Boolean} cancel true to cancel the query
39115              * @param {Object} e The query event object
39116              */
39117         'beforequery': true,
39118          /**
39119          * @event add
39120          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39121              * @param {Roo.form.ComboBox} combo This combo box
39122              */
39123         'add' : true,
39124         /**
39125          * @event edit
39126          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39127              * @param {Roo.form.ComboBox} combo This combo box
39128              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39129              */
39130         'edit' : true
39131         
39132         
39133     });
39134     if(this.transform){
39135         this.allowDomMove = false;
39136         var s = Roo.getDom(this.transform);
39137         if(!this.hiddenName){
39138             this.hiddenName = s.name;
39139         }
39140         if(!this.store){
39141             this.mode = 'local';
39142             var d = [], opts = s.options;
39143             for(var i = 0, len = opts.length;i < len; i++){
39144                 var o = opts[i];
39145                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39146                 if(o.selected) {
39147                     this.value = value;
39148                 }
39149                 d.push([value, o.text]);
39150             }
39151             this.store = new Roo.data.SimpleStore({
39152                 'id': 0,
39153                 fields: ['value', 'text'],
39154                 data : d
39155             });
39156             this.valueField = 'value';
39157             this.displayField = 'text';
39158         }
39159         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39160         if(!this.lazyRender){
39161             this.target = true;
39162             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39163             s.parentNode.removeChild(s); // remove it
39164             this.render(this.el.parentNode);
39165         }else{
39166             s.parentNode.removeChild(s); // remove it
39167         }
39168
39169     }
39170     if (this.store) {
39171         this.store = Roo.factory(this.store, Roo.data);
39172     }
39173     
39174     this.selectedIndex = -1;
39175     if(this.mode == 'local'){
39176         if(config.queryDelay === undefined){
39177             this.queryDelay = 10;
39178         }
39179         if(config.minChars === undefined){
39180             this.minChars = 0;
39181         }
39182     }
39183 };
39184
39185 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39186     /**
39187      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39188      */
39189     /**
39190      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39191      * rendering into an Roo.Editor, defaults to false)
39192      */
39193     /**
39194      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39195      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39196      */
39197     /**
39198      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39199      */
39200     /**
39201      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39202      * the dropdown list (defaults to undefined, with no header element)
39203      */
39204
39205      /**
39206      * @cfg {String/Roo.Template} tpl The template to use to render the output
39207      */
39208      
39209     // private
39210     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39211     /**
39212      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39213      */
39214     listWidth: undefined,
39215     /**
39216      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39217      * mode = 'remote' or 'text' if mode = 'local')
39218      */
39219     displayField: undefined,
39220     /**
39221      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39222      * mode = 'remote' or 'value' if mode = 'local'). 
39223      * Note: use of a valueField requires the user make a selection
39224      * in order for a value to be mapped.
39225      */
39226     valueField: undefined,
39227     
39228     
39229     /**
39230      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39231      * field's data value (defaults to the underlying DOM element's name)
39232      */
39233     hiddenName: undefined,
39234     /**
39235      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39236      */
39237     listClass: '',
39238     /**
39239      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39240      */
39241     selectedClass: 'x-combo-selected',
39242     /**
39243      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39244      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39245      * which displays a downward arrow icon).
39246      */
39247     triggerClass : 'x-form-arrow-trigger',
39248     /**
39249      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39250      */
39251     shadow:'sides',
39252     /**
39253      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39254      * anchor positions (defaults to 'tl-bl')
39255      */
39256     listAlign: 'tl-bl?',
39257     /**
39258      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39259      */
39260     maxHeight: 300,
39261     /**
39262      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39263      * query specified by the allQuery config option (defaults to 'query')
39264      */
39265     triggerAction: 'query',
39266     /**
39267      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39268      * (defaults to 4, does not apply if editable = false)
39269      */
39270     minChars : 4,
39271     /**
39272      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39273      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39274      */
39275     typeAhead: false,
39276     /**
39277      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39278      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39279      */
39280     queryDelay: 500,
39281     /**
39282      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39283      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39284      */
39285     pageSize: 0,
39286     /**
39287      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39288      * when editable = true (defaults to false)
39289      */
39290     selectOnFocus:false,
39291     /**
39292      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39293      */
39294     queryParam: 'query',
39295     /**
39296      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39297      * when mode = 'remote' (defaults to 'Loading...')
39298      */
39299     loadingText: 'Loading...',
39300     /**
39301      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39302      */
39303     resizable: false,
39304     /**
39305      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39306      */
39307     handleHeight : 8,
39308     /**
39309      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39310      * traditional select (defaults to true)
39311      */
39312     editable: true,
39313     /**
39314      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39315      */
39316     allQuery: '',
39317     /**
39318      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39319      */
39320     mode: 'remote',
39321     /**
39322      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39323      * listWidth has a higher value)
39324      */
39325     minListWidth : 70,
39326     /**
39327      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39328      * allow the user to set arbitrary text into the field (defaults to false)
39329      */
39330     forceSelection:false,
39331     /**
39332      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39333      * if typeAhead = true (defaults to 250)
39334      */
39335     typeAheadDelay : 250,
39336     /**
39337      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39338      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39339      */
39340     valueNotFoundText : undefined,
39341     /**
39342      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39343      */
39344     blockFocus : false,
39345     
39346     /**
39347      * @cfg {Boolean} disableClear Disable showing of clear button.
39348      */
39349     disableClear : false,
39350     /**
39351      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39352      */
39353     alwaysQuery : false,
39354     
39355     //private
39356     addicon : false,
39357     editicon: false,
39358     
39359     // element that contains real text value.. (when hidden is used..)
39360      
39361     // private
39362     onRender : function(ct, position){
39363         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39364         if(this.hiddenName){
39365             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39366                     'before', true);
39367             this.hiddenField.value =
39368                 this.hiddenValue !== undefined ? this.hiddenValue :
39369                 this.value !== undefined ? this.value : '';
39370
39371             // prevent input submission
39372             this.el.dom.removeAttribute('name');
39373              
39374              
39375         }
39376         if(Roo.isGecko){
39377             this.el.dom.setAttribute('autocomplete', 'off');
39378         }
39379
39380         var cls = 'x-combo-list';
39381
39382         this.list = new Roo.Layer({
39383             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39384         });
39385
39386         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39387         this.list.setWidth(lw);
39388         this.list.swallowEvent('mousewheel');
39389         this.assetHeight = 0;
39390
39391         if(this.title){
39392             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39393             this.assetHeight += this.header.getHeight();
39394         }
39395
39396         this.innerList = this.list.createChild({cls:cls+'-inner'});
39397         this.innerList.on('mouseover', this.onViewOver, this);
39398         this.innerList.on('mousemove', this.onViewMove, this);
39399         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39400         
39401         if(this.allowBlank && !this.pageSize && !this.disableClear){
39402             this.footer = this.list.createChild({cls:cls+'-ft'});
39403             this.pageTb = new Roo.Toolbar(this.footer);
39404            
39405         }
39406         if(this.pageSize){
39407             this.footer = this.list.createChild({cls:cls+'-ft'});
39408             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39409                     {pageSize: this.pageSize});
39410             
39411         }
39412         
39413         if (this.pageTb && this.allowBlank && !this.disableClear) {
39414             var _this = this;
39415             this.pageTb.add(new Roo.Toolbar.Fill(), {
39416                 cls: 'x-btn-icon x-btn-clear',
39417                 text: '&#160;',
39418                 handler: function()
39419                 {
39420                     _this.collapse();
39421                     _this.clearValue();
39422                     _this.onSelect(false, -1);
39423                 }
39424             });
39425         }
39426         if (this.footer) {
39427             this.assetHeight += this.footer.getHeight();
39428         }
39429         
39430
39431         if(!this.tpl){
39432             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39433         }
39434
39435         this.view = new Roo.View(this.innerList, this.tpl, {
39436             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39437         });
39438
39439         this.view.on('click', this.onViewClick, this);
39440
39441         this.store.on('beforeload', this.onBeforeLoad, this);
39442         this.store.on('load', this.onLoad, this);
39443         this.store.on('loadexception', this.onLoadException, this);
39444
39445         if(this.resizable){
39446             this.resizer = new Roo.Resizable(this.list,  {
39447                pinned:true, handles:'se'
39448             });
39449             this.resizer.on('resize', function(r, w, h){
39450                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39451                 this.listWidth = w;
39452                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39453                 this.restrictHeight();
39454             }, this);
39455             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39456         }
39457         if(!this.editable){
39458             this.editable = true;
39459             this.setEditable(false);
39460         }  
39461         
39462         
39463         if (typeof(this.events.add.listeners) != 'undefined') {
39464             
39465             this.addicon = this.wrap.createChild(
39466                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39467        
39468             this.addicon.on('click', function(e) {
39469                 this.fireEvent('add', this);
39470             }, this);
39471         }
39472         if (typeof(this.events.edit.listeners) != 'undefined') {
39473             
39474             this.editicon = this.wrap.createChild(
39475                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39476             if (this.addicon) {
39477                 this.editicon.setStyle('margin-left', '40px');
39478             }
39479             this.editicon.on('click', function(e) {
39480                 
39481                 // we fire even  if inothing is selected..
39482                 this.fireEvent('edit', this, this.lastData );
39483                 
39484             }, this);
39485         }
39486         
39487         
39488         
39489     },
39490
39491     // private
39492     initEvents : function(){
39493         Roo.form.ComboBox.superclass.initEvents.call(this);
39494
39495         this.keyNav = new Roo.KeyNav(this.el, {
39496             "up" : function(e){
39497                 this.inKeyMode = true;
39498                 this.selectPrev();
39499             },
39500
39501             "down" : function(e){
39502                 if(!this.isExpanded()){
39503                     this.onTriggerClick();
39504                 }else{
39505                     this.inKeyMode = true;
39506                     this.selectNext();
39507                 }
39508             },
39509
39510             "enter" : function(e){
39511                 this.onViewClick();
39512                 //return true;
39513             },
39514
39515             "esc" : function(e){
39516                 this.collapse();
39517             },
39518
39519             "tab" : function(e){
39520                 this.onViewClick(false);
39521                 this.fireEvent("specialkey", this, e);
39522                 return true;
39523             },
39524
39525             scope : this,
39526
39527             doRelay : function(foo, bar, hname){
39528                 if(hname == 'down' || this.scope.isExpanded()){
39529                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39530                 }
39531                 return true;
39532             },
39533
39534             forceKeyDown: true
39535         });
39536         this.queryDelay = Math.max(this.queryDelay || 10,
39537                 this.mode == 'local' ? 10 : 250);
39538         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39539         if(this.typeAhead){
39540             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39541         }
39542         if(this.editable !== false){
39543             this.el.on("keyup", this.onKeyUp, this);
39544         }
39545         if(this.forceSelection){
39546             this.on('blur', this.doForce, this);
39547         }
39548     },
39549
39550     onDestroy : function(){
39551         if(this.view){
39552             this.view.setStore(null);
39553             this.view.el.removeAllListeners();
39554             this.view.el.remove();
39555             this.view.purgeListeners();
39556         }
39557         if(this.list){
39558             this.list.destroy();
39559         }
39560         if(this.store){
39561             this.store.un('beforeload', this.onBeforeLoad, this);
39562             this.store.un('load', this.onLoad, this);
39563             this.store.un('loadexception', this.onLoadException, this);
39564         }
39565         Roo.form.ComboBox.superclass.onDestroy.call(this);
39566     },
39567
39568     // private
39569     fireKey : function(e){
39570         if(e.isNavKeyPress() && !this.list.isVisible()){
39571             this.fireEvent("specialkey", this, e);
39572         }
39573     },
39574
39575     // private
39576     onResize: function(w, h){
39577         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39578         
39579         if(typeof w != 'number'){
39580             // we do not handle it!?!?
39581             return;
39582         }
39583         var tw = this.trigger.getWidth();
39584         tw += this.addicon ? this.addicon.getWidth() : 0;
39585         tw += this.editicon ? this.editicon.getWidth() : 0;
39586         var x = w - tw;
39587         this.el.setWidth( this.adjustWidth('input', x));
39588             
39589         this.trigger.setStyle('left', x+'px');
39590         
39591         if(this.list && this.listWidth === undefined){
39592             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39593             this.list.setWidth(lw);
39594             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39595         }
39596         
39597     
39598         
39599     },
39600
39601     /**
39602      * Allow or prevent the user from directly editing the field text.  If false is passed,
39603      * the user will only be able to select from the items defined in the dropdown list.  This method
39604      * is the runtime equivalent of setting the 'editable' config option at config time.
39605      * @param {Boolean} value True to allow the user to directly edit the field text
39606      */
39607     setEditable : function(value){
39608         if(value == this.editable){
39609             return;
39610         }
39611         this.editable = value;
39612         if(!value){
39613             this.el.dom.setAttribute('readOnly', true);
39614             this.el.on('mousedown', this.onTriggerClick,  this);
39615             this.el.addClass('x-combo-noedit');
39616         }else{
39617             this.el.dom.setAttribute('readOnly', false);
39618             this.el.un('mousedown', this.onTriggerClick,  this);
39619             this.el.removeClass('x-combo-noedit');
39620         }
39621     },
39622
39623     // private
39624     onBeforeLoad : function(){
39625         if(!this.hasFocus){
39626             return;
39627         }
39628         this.innerList.update(this.loadingText ?
39629                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39630         this.restrictHeight();
39631         this.selectedIndex = -1;
39632     },
39633
39634     // private
39635     onLoad : function(){
39636         if(!this.hasFocus){
39637             return;
39638         }
39639         if(this.store.getCount() > 0){
39640             this.expand();
39641             this.restrictHeight();
39642             if(this.lastQuery == this.allQuery){
39643                 if(this.editable){
39644                     this.el.dom.select();
39645                 }
39646                 if(!this.selectByValue(this.value, true)){
39647                     this.select(0, true);
39648                 }
39649             }else{
39650                 this.selectNext();
39651                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39652                     this.taTask.delay(this.typeAheadDelay);
39653                 }
39654             }
39655         }else{
39656             this.onEmptyResults();
39657         }
39658         //this.el.focus();
39659     },
39660     // private
39661     onLoadException : function()
39662     {
39663         this.collapse();
39664         Roo.log(this.store.reader.jsonData);
39665         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39666             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39667         }
39668         
39669         
39670     },
39671     // private
39672     onTypeAhead : function(){
39673         if(this.store.getCount() > 0){
39674             var r = this.store.getAt(0);
39675             var newValue = r.data[this.displayField];
39676             var len = newValue.length;
39677             var selStart = this.getRawValue().length;
39678             if(selStart != len){
39679                 this.setRawValue(newValue);
39680                 this.selectText(selStart, newValue.length);
39681             }
39682         }
39683     },
39684
39685     // private
39686     onSelect : function(record, index){
39687         if(this.fireEvent('beforeselect', this, record, index) !== false){
39688             this.setFromData(index > -1 ? record.data : false);
39689             this.collapse();
39690             this.fireEvent('select', this, record, index);
39691         }
39692     },
39693
39694     /**
39695      * Returns the currently selected field value or empty string if no value is set.
39696      * @return {String} value The selected value
39697      */
39698     getValue : function(){
39699         if(this.valueField){
39700             return typeof this.value != 'undefined' ? this.value : '';
39701         }else{
39702             return Roo.form.ComboBox.superclass.getValue.call(this);
39703         }
39704     },
39705
39706     /**
39707      * Clears any text/value currently set in the field
39708      */
39709     clearValue : function(){
39710         if(this.hiddenField){
39711             this.hiddenField.value = '';
39712         }
39713         this.value = '';
39714         this.setRawValue('');
39715         this.lastSelectionText = '';
39716         
39717     },
39718
39719     /**
39720      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39721      * will be displayed in the field.  If the value does not match the data value of an existing item,
39722      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39723      * Otherwise the field will be blank (although the value will still be set).
39724      * @param {String} value The value to match
39725      */
39726     setValue : function(v){
39727         var text = v;
39728         if(this.valueField){
39729             var r = this.findRecord(this.valueField, v);
39730             if(r){
39731                 text = r.data[this.displayField];
39732             }else if(this.valueNotFoundText !== undefined){
39733                 text = this.valueNotFoundText;
39734             }
39735         }
39736         this.lastSelectionText = text;
39737         if(this.hiddenField){
39738             this.hiddenField.value = v;
39739         }
39740         Roo.form.ComboBox.superclass.setValue.call(this, text);
39741         this.value = v;
39742     },
39743     /**
39744      * @property {Object} the last set data for the element
39745      */
39746     
39747     lastData : false,
39748     /**
39749      * Sets the value of the field based on a object which is related to the record format for the store.
39750      * @param {Object} value the value to set as. or false on reset?
39751      */
39752     setFromData : function(o){
39753         var dv = ''; // display value
39754         var vv = ''; // value value..
39755         this.lastData = o;
39756         if (this.displayField) {
39757             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39758         } else {
39759             // this is an error condition!!!
39760             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39761         }
39762         
39763         if(this.valueField){
39764             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39765         }
39766         if(this.hiddenField){
39767             this.hiddenField.value = vv;
39768             
39769             this.lastSelectionText = dv;
39770             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39771             this.value = vv;
39772             return;
39773         }
39774         // no hidden field.. - we store the value in 'value', but still display
39775         // display field!!!!
39776         this.lastSelectionText = dv;
39777         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39778         this.value = vv;
39779         
39780         
39781     },
39782     // private
39783     reset : function(){
39784         // overridden so that last data is reset..
39785         this.setValue(this.resetValue);
39786         this.clearInvalid();
39787         this.lastData = false;
39788         if (this.view) {
39789             this.view.clearSelections();
39790         }
39791     },
39792     // private
39793     findRecord : function(prop, value){
39794         var record;
39795         if(this.store.getCount() > 0){
39796             this.store.each(function(r){
39797                 if(r.data[prop] == value){
39798                     record = r;
39799                     return false;
39800                 }
39801                 return true;
39802             });
39803         }
39804         return record;
39805     },
39806     
39807     getName: function()
39808     {
39809         // returns hidden if it's set..
39810         if (!this.rendered) {return ''};
39811         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39812         
39813     },
39814     // private
39815     onViewMove : function(e, t){
39816         this.inKeyMode = false;
39817     },
39818
39819     // private
39820     onViewOver : function(e, t){
39821         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39822             return;
39823         }
39824         var item = this.view.findItemFromChild(t);
39825         if(item){
39826             var index = this.view.indexOf(item);
39827             this.select(index, false);
39828         }
39829     },
39830
39831     // private
39832     onViewClick : function(doFocus)
39833     {
39834         var index = this.view.getSelectedIndexes()[0];
39835         var r = this.store.getAt(index);
39836         if(r){
39837             this.onSelect(r, index);
39838         }
39839         if(doFocus !== false && !this.blockFocus){
39840             this.el.focus();
39841         }
39842     },
39843
39844     // private
39845     restrictHeight : function(){
39846         this.innerList.dom.style.height = '';
39847         var inner = this.innerList.dom;
39848         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39849         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39850         this.list.beginUpdate();
39851         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39852         this.list.alignTo(this.el, this.listAlign);
39853         this.list.endUpdate();
39854     },
39855
39856     // private
39857     onEmptyResults : function(){
39858         this.collapse();
39859     },
39860
39861     /**
39862      * Returns true if the dropdown list is expanded, else false.
39863      */
39864     isExpanded : function(){
39865         return this.list.isVisible();
39866     },
39867
39868     /**
39869      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39870      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39871      * @param {String} value The data value of the item to select
39872      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39873      * selected item if it is not currently in view (defaults to true)
39874      * @return {Boolean} True if the value matched an item in the list, else false
39875      */
39876     selectByValue : function(v, scrollIntoView){
39877         if(v !== undefined && v !== null){
39878             var r = this.findRecord(this.valueField || this.displayField, v);
39879             if(r){
39880                 this.select(this.store.indexOf(r), scrollIntoView);
39881                 return true;
39882             }
39883         }
39884         return false;
39885     },
39886
39887     /**
39888      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39889      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39890      * @param {Number} index The zero-based index of the list item to select
39891      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39892      * selected item if it is not currently in view (defaults to true)
39893      */
39894     select : function(index, scrollIntoView){
39895         this.selectedIndex = index;
39896         this.view.select(index);
39897         if(scrollIntoView !== false){
39898             var el = this.view.getNode(index);
39899             if(el){
39900                 this.innerList.scrollChildIntoView(el, false);
39901             }
39902         }
39903     },
39904
39905     // private
39906     selectNext : function(){
39907         var ct = this.store.getCount();
39908         if(ct > 0){
39909             if(this.selectedIndex == -1){
39910                 this.select(0);
39911             }else if(this.selectedIndex < ct-1){
39912                 this.select(this.selectedIndex+1);
39913             }
39914         }
39915     },
39916
39917     // private
39918     selectPrev : function(){
39919         var ct = this.store.getCount();
39920         if(ct > 0){
39921             if(this.selectedIndex == -1){
39922                 this.select(0);
39923             }else if(this.selectedIndex != 0){
39924                 this.select(this.selectedIndex-1);
39925             }
39926         }
39927     },
39928
39929     // private
39930     onKeyUp : function(e){
39931         if(this.editable !== false && !e.isSpecialKey()){
39932             this.lastKey = e.getKey();
39933             this.dqTask.delay(this.queryDelay);
39934         }
39935     },
39936
39937     // private
39938     validateBlur : function(){
39939         return !this.list || !this.list.isVisible();   
39940     },
39941
39942     // private
39943     initQuery : function(){
39944         this.doQuery(this.getRawValue());
39945     },
39946
39947     // private
39948     doForce : function(){
39949         if(this.el.dom.value.length > 0){
39950             this.el.dom.value =
39951                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39952              
39953         }
39954     },
39955
39956     /**
39957      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39958      * query allowing the query action to be canceled if needed.
39959      * @param {String} query The SQL query to execute
39960      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39961      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39962      * saved in the current store (defaults to false)
39963      */
39964     doQuery : function(q, forceAll){
39965         if(q === undefined || q === null){
39966             q = '';
39967         }
39968         var qe = {
39969             query: q,
39970             forceAll: forceAll,
39971             combo: this,
39972             cancel:false
39973         };
39974         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39975             return false;
39976         }
39977         q = qe.query;
39978         forceAll = qe.forceAll;
39979         if(forceAll === true || (q.length >= this.minChars)){
39980             if(this.lastQuery != q || this.alwaysQuery){
39981                 this.lastQuery = q;
39982                 if(this.mode == 'local'){
39983                     this.selectedIndex = -1;
39984                     if(forceAll){
39985                         this.store.clearFilter();
39986                     }else{
39987                         this.store.filter(this.displayField, q);
39988                     }
39989                     this.onLoad();
39990                 }else{
39991                     this.store.baseParams[this.queryParam] = q;
39992                     this.store.load({
39993                         params: this.getParams(q)
39994                     });
39995                     this.expand();
39996                 }
39997             }else{
39998                 this.selectedIndex = -1;
39999                 this.onLoad();   
40000             }
40001         }
40002     },
40003
40004     // private
40005     getParams : function(q){
40006         var p = {};
40007         //p[this.queryParam] = q;
40008         if(this.pageSize){
40009             p.start = 0;
40010             p.limit = this.pageSize;
40011         }
40012         return p;
40013     },
40014
40015     /**
40016      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40017      */
40018     collapse : function(){
40019         if(!this.isExpanded()){
40020             return;
40021         }
40022         this.list.hide();
40023         Roo.get(document).un('mousedown', this.collapseIf, this);
40024         Roo.get(document).un('mousewheel', this.collapseIf, this);
40025         if (!this.editable) {
40026             Roo.get(document).un('keydown', this.listKeyPress, this);
40027         }
40028         this.fireEvent('collapse', this);
40029     },
40030
40031     // private
40032     collapseIf : function(e){
40033         if(!e.within(this.wrap) && !e.within(this.list)){
40034             this.collapse();
40035         }
40036     },
40037
40038     /**
40039      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40040      */
40041     expand : function(){
40042         if(this.isExpanded() || !this.hasFocus){
40043             return;
40044         }
40045         this.list.alignTo(this.el, this.listAlign);
40046         this.list.show();
40047         Roo.get(document).on('mousedown', this.collapseIf, this);
40048         Roo.get(document).on('mousewheel', this.collapseIf, this);
40049         if (!this.editable) {
40050             Roo.get(document).on('keydown', this.listKeyPress, this);
40051         }
40052         
40053         this.fireEvent('expand', this);
40054     },
40055
40056     // private
40057     // Implements the default empty TriggerField.onTriggerClick function
40058     onTriggerClick : function(){
40059         if(this.disabled){
40060             return;
40061         }
40062         if(this.isExpanded()){
40063             this.collapse();
40064             if (!this.blockFocus) {
40065                 this.el.focus();
40066             }
40067             
40068         }else {
40069             this.hasFocus = true;
40070             if(this.triggerAction == 'all') {
40071                 this.doQuery(this.allQuery, true);
40072             } else {
40073                 this.doQuery(this.getRawValue());
40074             }
40075             if (!this.blockFocus) {
40076                 this.el.focus();
40077             }
40078         }
40079     },
40080     listKeyPress : function(e)
40081     {
40082         //Roo.log('listkeypress');
40083         // scroll to first matching element based on key pres..
40084         if (e.isSpecialKey()) {
40085             return false;
40086         }
40087         var k = String.fromCharCode(e.getKey()).toUpperCase();
40088         //Roo.log(k);
40089         var match  = false;
40090         var csel = this.view.getSelectedNodes();
40091         var cselitem = false;
40092         if (csel.length) {
40093             var ix = this.view.indexOf(csel[0]);
40094             cselitem  = this.store.getAt(ix);
40095             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40096                 cselitem = false;
40097             }
40098             
40099         }
40100         
40101         this.store.each(function(v) { 
40102             if (cselitem) {
40103                 // start at existing selection.
40104                 if (cselitem.id == v.id) {
40105                     cselitem = false;
40106                 }
40107                 return;
40108             }
40109                 
40110             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40111                 match = this.store.indexOf(v);
40112                 return false;
40113             }
40114         }, this);
40115         
40116         if (match === false) {
40117             return true; // no more action?
40118         }
40119         // scroll to?
40120         this.view.select(match);
40121         var sn = Roo.get(this.view.getSelectedNodes()[0])
40122         sn.scrollIntoView(sn.dom.parentNode, false);
40123     }
40124
40125     /** 
40126     * @cfg {Boolean} grow 
40127     * @hide 
40128     */
40129     /** 
40130     * @cfg {Number} growMin 
40131     * @hide 
40132     */
40133     /** 
40134     * @cfg {Number} growMax 
40135     * @hide 
40136     */
40137     /**
40138      * @hide
40139      * @method autoSize
40140      */
40141 });/*
40142  * Copyright(c) 2010-2012, Roo J Solutions Limited
40143  *
40144  * Licence LGPL
40145  *
40146  */
40147
40148 /**
40149  * @class Roo.form.ComboBoxArray
40150  * @extends Roo.form.TextField
40151  * A facebook style adder... for lists of email / people / countries  etc...
40152  * pick multiple items from a combo box, and shows each one.
40153  *
40154  *  Fred [x]  Brian [x]  [Pick another |v]
40155  *
40156  *
40157  *  For this to work: it needs various extra information
40158  *    - normal combo problay has
40159  *      name, hiddenName
40160  *    + displayField, valueField
40161  *
40162  *    For our purpose...
40163  *
40164  *
40165  *   If we change from 'extends' to wrapping...
40166  *   
40167  *  
40168  *
40169  
40170  
40171  * @constructor
40172  * Create a new ComboBoxArray.
40173  * @param {Object} config Configuration options
40174  */
40175  
40176
40177 Roo.form.ComboBoxArray = function(config)
40178 {
40179     this.addEvents({
40180         /**
40181          * @event remove
40182          * Fires when remove the value from the list
40183              * @param {Roo.form.ComboBoxArray} _self This combo box array
40184              * @param {Roo.form.ComboBoxArray.Item} item removed item
40185              */
40186         'remove' : true
40187         
40188         
40189     });
40190     
40191     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40192     
40193     this.items = new Roo.util.MixedCollection(false);
40194     
40195     // construct the child combo...
40196     
40197     
40198     
40199     
40200    
40201     
40202 }
40203
40204  
40205 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40206
40207     /**
40208      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40209      */
40210     
40211     lastData : false,
40212     
40213     // behavies liek a hiddne field
40214     inputType:      'hidden',
40215     /**
40216      * @cfg {Number} width The width of the box that displays the selected element
40217      */ 
40218     width:          300,
40219
40220     
40221     
40222     /**
40223      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40224      */
40225     name : false,
40226     /**
40227      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40228      */
40229     hiddenName : false,
40230     
40231     
40232     // private the array of items that are displayed..
40233     items  : false,
40234     // private - the hidden field el.
40235     hiddenEl : false,
40236     // private - the filed el..
40237     el : false,
40238     
40239     //validateValue : function() { return true; }, // all values are ok!
40240     //onAddClick: function() { },
40241     
40242     onRender : function(ct, position) 
40243     {
40244         
40245         // create the standard hidden element
40246         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40247         
40248         
40249         // give fake names to child combo;
40250         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40251         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40252         
40253         this.combo = Roo.factory(this.combo, Roo.form);
40254         this.combo.onRender(ct, position);
40255         if (typeof(this.combo.width) != 'undefined') {
40256             this.combo.onResize(this.combo.width,0);
40257         }
40258         
40259         this.combo.initEvents();
40260         
40261         // assigned so form know we need to do this..
40262         this.store          = this.combo.store;
40263         this.valueField     = this.combo.valueField;
40264         this.displayField   = this.combo.displayField ;
40265         
40266         
40267         this.combo.wrap.addClass('x-cbarray-grp');
40268         
40269         var cbwrap = this.combo.wrap.createChild(
40270             {tag: 'div', cls: 'x-cbarray-cb'},
40271             this.combo.el.dom
40272         );
40273         
40274              
40275         this.hiddenEl = this.combo.wrap.createChild({
40276             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40277         });
40278         this.el = this.combo.wrap.createChild({
40279             tag: 'input',  type:'hidden' , name: this.name, value : ''
40280         });
40281          //   this.el.dom.removeAttribute("name");
40282         
40283         
40284         this.outerWrap = this.combo.wrap;
40285         this.wrap = cbwrap;
40286         
40287         this.outerWrap.setWidth(this.width);
40288         this.outerWrap.dom.removeChild(this.el.dom);
40289         
40290         this.wrap.dom.appendChild(this.el.dom);
40291         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40292         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40293         
40294         this.combo.trigger.setStyle('position','relative');
40295         this.combo.trigger.setStyle('left', '0px');
40296         this.combo.trigger.setStyle('top', '2px');
40297         
40298         this.combo.el.setStyle('vertical-align', 'text-bottom');
40299         
40300         //this.trigger.setStyle('vertical-align', 'top');
40301         
40302         // this should use the code from combo really... on('add' ....)
40303         if (this.adder) {
40304             
40305         
40306             this.adder = this.outerWrap.createChild(
40307                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40308             var _t = this;
40309             this.adder.on('click', function(e) {
40310                 _t.fireEvent('adderclick', this, e);
40311             }, _t);
40312         }
40313         //var _t = this;
40314         //this.adder.on('click', this.onAddClick, _t);
40315         
40316         
40317         this.combo.on('select', function(cb, rec, ix) {
40318             this.addItem(rec.data);
40319             
40320             cb.setValue('');
40321             cb.el.dom.value = '';
40322             //cb.lastData = rec.data;
40323             // add to list
40324             
40325         }, this);
40326         
40327         
40328     },
40329     
40330     
40331     getName: function()
40332     {
40333         // returns hidden if it's set..
40334         if (!this.rendered) {return ''};
40335         return  this.hiddenName ? this.hiddenName : this.name;
40336         
40337     },
40338     
40339     
40340     onResize: function(w, h){
40341         
40342         return;
40343         // not sure if this is needed..
40344         //this.combo.onResize(w,h);
40345         
40346         if(typeof w != 'number'){
40347             // we do not handle it!?!?
40348             return;
40349         }
40350         var tw = this.combo.trigger.getWidth();
40351         tw += this.addicon ? this.addicon.getWidth() : 0;
40352         tw += this.editicon ? this.editicon.getWidth() : 0;
40353         var x = w - tw;
40354         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40355             
40356         this.combo.trigger.setStyle('left', '0px');
40357         
40358         if(this.list && this.listWidth === undefined){
40359             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40360             this.list.setWidth(lw);
40361             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40362         }
40363         
40364     
40365         
40366     },
40367     
40368     addItem: function(rec)
40369     {
40370         var valueField = this.combo.valueField;
40371         var displayField = this.combo.displayField;
40372         if (this.items.indexOfKey(rec[valueField]) > -1) {
40373             //console.log("GOT " + rec.data.id);
40374             return;
40375         }
40376         
40377         var x = new Roo.form.ComboBoxArray.Item({
40378             //id : rec[this.idField],
40379             data : rec,
40380             displayField : displayField ,
40381             tipField : displayField ,
40382             cb : this
40383         });
40384         // use the 
40385         this.items.add(rec[valueField],x);
40386         // add it before the element..
40387         this.updateHiddenEl();
40388         x.render(this.outerWrap, this.wrap.dom);
40389         // add the image handler..
40390     },
40391     
40392     updateHiddenEl : function()
40393     {
40394         this.validate();
40395         if (!this.hiddenEl) {
40396             return;
40397         }
40398         var ar = [];
40399         var idField = this.combo.valueField;
40400         
40401         this.items.each(function(f) {
40402             ar.push(f.data[idField]);
40403            
40404         });
40405         this.hiddenEl.dom.value = ar.join(',');
40406         this.validate();
40407     },
40408     
40409     reset : function()
40410     {
40411         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40412         this.items.each(function(f) {
40413            f.remove(); 
40414         });
40415         this.el.dom.value = '';
40416         if (this.hiddenEl) {
40417             this.hiddenEl.dom.value = '';
40418         }
40419         
40420     },
40421     getValue: function()
40422     {
40423         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40424     },
40425     setValue: function(v) // not a valid action - must use addItems..
40426     {
40427          
40428         this.reset();
40429         
40430         
40431         
40432         if (this.store.isLocal && (typeof(v) == 'string')) {
40433             // then we can use the store to find the values..
40434             // comma seperated at present.. this needs to allow JSON based encoding..
40435             this.hiddenEl.value  = v;
40436             var v_ar = [];
40437             Roo.each(v.split(','), function(k) {
40438                 Roo.log("CHECK " + this.valueField + ',' + k);
40439                 var li = this.store.query(this.valueField, k);
40440                 if (!li.length) {
40441                     return;
40442                 }
40443                 var add = {};
40444                 add[this.valueField] = k;
40445                 add[this.displayField] = li.item(0).data[this.displayField];
40446                 
40447                 this.addItem(add);
40448             }, this) 
40449              
40450         }
40451         if (typeof(v) == 'object') {
40452             // then let's assume it's an array of objects..
40453             Roo.each(v, function(l) {
40454                 this.addItem(l);
40455             }, this);
40456              
40457         }
40458         
40459         
40460     },
40461     setFromData: function(v)
40462     {
40463         // this recieves an object, if setValues is called.
40464         this.reset();
40465         this.el.dom.value = v[this.displayField];
40466         this.hiddenEl.dom.value = v[this.valueField];
40467         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40468             return;
40469         }
40470         var kv = v[this.valueField];
40471         var dv = v[this.displayField];
40472         kv = typeof(kv) != 'string' ? '' : kv;
40473         dv = typeof(dv) != 'string' ? '' : dv;
40474         
40475         
40476         var keys = kv.split(',');
40477         var display = dv.split(',');
40478         for (var i = 0 ; i < keys.length; i++) {
40479             
40480             add = {};
40481             add[this.valueField] = keys[i];
40482             add[this.displayField] = display[i];
40483             this.addItem(add);
40484         }
40485       
40486         
40487     },
40488     
40489     /**
40490      * Validates the combox array value
40491      * @return {Boolean} True if the value is valid, else false
40492      */
40493     validate : function(){
40494         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40495             this.clearInvalid();
40496             return true;
40497         }
40498         return false;
40499     },
40500     
40501     validateValue : function(value){
40502         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40503         
40504     },
40505     
40506     /*@
40507      * overide
40508      * 
40509      */
40510     isDirty : function() {
40511         if(this.disabled) {
40512             return false;
40513         }
40514         
40515         try {
40516             var d = Roo.decode(String(this.originalValue));
40517         } catch (e) {
40518             return String(this.getValue()) !== String(this.originalValue);
40519         }
40520         
40521         var originalValue = [];
40522         
40523         for (var i = 0; i < d.length; i++){
40524             originalValue.push(d[i][this.valueField]);
40525         }
40526         
40527         return String(this.getValue()) !== String(originalValue.join(','));
40528         
40529     }
40530     
40531 });
40532
40533
40534
40535 /**
40536  * @class Roo.form.ComboBoxArray.Item
40537  * @extends Roo.BoxComponent
40538  * A selected item in the list
40539  *  Fred [x]  Brian [x]  [Pick another |v]
40540  * 
40541  * @constructor
40542  * Create a new item.
40543  * @param {Object} config Configuration options
40544  */
40545  
40546 Roo.form.ComboBoxArray.Item = function(config) {
40547     config.id = Roo.id();
40548     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40549 }
40550
40551 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40552     data : {},
40553     cb: false,
40554     displayField : false,
40555     tipField : false,
40556     
40557     
40558     defaultAutoCreate : {
40559         tag: 'div',
40560         cls: 'x-cbarray-item',
40561         cn : [ 
40562             { tag: 'div' },
40563             {
40564                 tag: 'img',
40565                 width:16,
40566                 height : 16,
40567                 src : Roo.BLANK_IMAGE_URL ,
40568                 align: 'center'
40569             }
40570         ]
40571         
40572     },
40573     
40574  
40575     onRender : function(ct, position)
40576     {
40577         Roo.form.Field.superclass.onRender.call(this, ct, position);
40578         
40579         if(!this.el){
40580             var cfg = this.getAutoCreate();
40581             this.el = ct.createChild(cfg, position);
40582         }
40583         
40584         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40585         
40586         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40587             this.cb.renderer(this.data) :
40588             String.format('{0}',this.data[this.displayField]);
40589         
40590             
40591         this.el.child('div').dom.setAttribute('qtip',
40592                         String.format('{0}',this.data[this.tipField])
40593         );
40594         
40595         this.el.child('img').on('click', this.remove, this);
40596         
40597     },
40598    
40599     remove : function()
40600     {
40601         this.cb.items.remove(this);
40602         this.el.child('img').un('click', this.remove, this);
40603         this.el.remove();
40604         this.cb.updateHiddenEl();
40605         
40606         this.cb.fireEvent('remove', this.cb, this);
40607     }
40608 });/*
40609  * Based on:
40610  * Ext JS Library 1.1.1
40611  * Copyright(c) 2006-2007, Ext JS, LLC.
40612  *
40613  * Originally Released Under LGPL - original licence link has changed is not relivant.
40614  *
40615  * Fork - LGPL
40616  * <script type="text/javascript">
40617  */
40618 /**
40619  * @class Roo.form.Checkbox
40620  * @extends Roo.form.Field
40621  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40622  * @constructor
40623  * Creates a new Checkbox
40624  * @param {Object} config Configuration options
40625  */
40626 Roo.form.Checkbox = function(config){
40627     Roo.form.Checkbox.superclass.constructor.call(this, config);
40628     this.addEvents({
40629         /**
40630          * @event check
40631          * Fires when the checkbox is checked or unchecked.
40632              * @param {Roo.form.Checkbox} this This checkbox
40633              * @param {Boolean} checked The new checked value
40634              */
40635         check : true
40636     });
40637 };
40638
40639 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40640     /**
40641      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40642      */
40643     focusClass : undefined,
40644     /**
40645      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40646      */
40647     fieldClass: "x-form-field",
40648     /**
40649      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40650      */
40651     checked: false,
40652     /**
40653      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40654      * {tag: "input", type: "checkbox", autocomplete: "off"})
40655      */
40656     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40657     /**
40658      * @cfg {String} boxLabel The text that appears beside the checkbox
40659      */
40660     boxLabel : "",
40661     /**
40662      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40663      */  
40664     inputValue : '1',
40665     /**
40666      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40667      */
40668      valueOff: '0', // value when not checked..
40669
40670     actionMode : 'viewEl', 
40671     //
40672     // private
40673     itemCls : 'x-menu-check-item x-form-item',
40674     groupClass : 'x-menu-group-item',
40675     inputType : 'hidden',
40676     
40677     
40678     inSetChecked: false, // check that we are not calling self...
40679     
40680     inputElement: false, // real input element?
40681     basedOn: false, // ????
40682     
40683     isFormField: true, // not sure where this is needed!!!!
40684
40685     onResize : function(){
40686         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40687         if(!this.boxLabel){
40688             this.el.alignTo(this.wrap, 'c-c');
40689         }
40690     },
40691
40692     initEvents : function(){
40693         Roo.form.Checkbox.superclass.initEvents.call(this);
40694         this.el.on("click", this.onClick,  this);
40695         this.el.on("change", this.onClick,  this);
40696     },
40697
40698
40699     getResizeEl : function(){
40700         return this.wrap;
40701     },
40702
40703     getPositionEl : function(){
40704         return this.wrap;
40705     },
40706
40707     // private
40708     onRender : function(ct, position){
40709         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40710         /*
40711         if(this.inputValue !== undefined){
40712             this.el.dom.value = this.inputValue;
40713         }
40714         */
40715         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40716         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40717         var viewEl = this.wrap.createChild({ 
40718             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40719         this.viewEl = viewEl;   
40720         this.wrap.on('click', this.onClick,  this); 
40721         
40722         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40723         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40724         
40725         
40726         
40727         if(this.boxLabel){
40728             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40729         //    viewEl.on('click', this.onClick,  this); 
40730         }
40731         //if(this.checked){
40732             this.setChecked(this.checked);
40733         //}else{
40734             //this.checked = this.el.dom;
40735         //}
40736
40737     },
40738
40739     // private
40740     initValue : Roo.emptyFn,
40741
40742     /**
40743      * Returns the checked state of the checkbox.
40744      * @return {Boolean} True if checked, else false
40745      */
40746     getValue : function(){
40747         if(this.el){
40748             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40749         }
40750         return this.valueOff;
40751         
40752     },
40753
40754         // private
40755     onClick : function(){ 
40756         this.setChecked(!this.checked);
40757
40758         //if(this.el.dom.checked != this.checked){
40759         //    this.setValue(this.el.dom.checked);
40760        // }
40761     },
40762
40763     /**
40764      * Sets the checked state of the checkbox.
40765      * On is always based on a string comparison between inputValue and the param.
40766      * @param {Boolean/String} value - the value to set 
40767      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40768      */
40769     setValue : function(v,suppressEvent){
40770         
40771         
40772         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40773         //if(this.el && this.el.dom){
40774         //    this.el.dom.checked = this.checked;
40775         //    this.el.dom.defaultChecked = this.checked;
40776         //}
40777         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40778         //this.fireEvent("check", this, this.checked);
40779     },
40780     // private..
40781     setChecked : function(state,suppressEvent)
40782     {
40783         if (this.inSetChecked) {
40784             this.checked = state;
40785             return;
40786         }
40787         
40788     
40789         if(this.wrap){
40790             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40791         }
40792         this.checked = state;
40793         if(suppressEvent !== true){
40794             this.fireEvent('check', this, state);
40795         }
40796         this.inSetChecked = true;
40797         this.el.dom.value = state ? this.inputValue : this.valueOff;
40798         this.inSetChecked = false;
40799         
40800     },
40801     // handle setting of hidden value by some other method!!?!?
40802     setFromHidden: function()
40803     {
40804         if(!this.el){
40805             return;
40806         }
40807         //console.log("SET FROM HIDDEN");
40808         //alert('setFrom hidden');
40809         this.setValue(this.el.dom.value);
40810     },
40811     
40812     onDestroy : function()
40813     {
40814         if(this.viewEl){
40815             Roo.get(this.viewEl).remove();
40816         }
40817          
40818         Roo.form.Checkbox.superclass.onDestroy.call(this);
40819     }
40820
40821 });/*
40822  * Based on:
40823  * Ext JS Library 1.1.1
40824  * Copyright(c) 2006-2007, Ext JS, LLC.
40825  *
40826  * Originally Released Under LGPL - original licence link has changed is not relivant.
40827  *
40828  * Fork - LGPL
40829  * <script type="text/javascript">
40830  */
40831  
40832 /**
40833  * @class Roo.form.Radio
40834  * @extends Roo.form.Checkbox
40835  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40836  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40837  * @constructor
40838  * Creates a new Radio
40839  * @param {Object} config Configuration options
40840  */
40841 Roo.form.Radio = function(){
40842     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40843 };
40844 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40845     inputType: 'radio',
40846
40847     /**
40848      * If this radio is part of a group, it will return the selected value
40849      * @return {String}
40850      */
40851     getGroupValue : function(){
40852         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40853     },
40854     
40855     
40856     onRender : function(ct, position){
40857         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40858         
40859         if(this.inputValue !== undefined){
40860             this.el.dom.value = this.inputValue;
40861         }
40862          
40863         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40864         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40865         //var viewEl = this.wrap.createChild({ 
40866         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40867         //this.viewEl = viewEl;   
40868         //this.wrap.on('click', this.onClick,  this); 
40869         
40870         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40871         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40872         
40873         
40874         
40875         if(this.boxLabel){
40876             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40877         //    viewEl.on('click', this.onClick,  this); 
40878         }
40879          if(this.checked){
40880             this.el.dom.checked =   'checked' ;
40881         }
40882          
40883     } 
40884     
40885     
40886 });//<script type="text/javascript">
40887
40888 /*
40889  * Based  Ext JS Library 1.1.1
40890  * Copyright(c) 2006-2007, Ext JS, LLC.
40891  * LGPL
40892  *
40893  */
40894  
40895 /**
40896  * @class Roo.HtmlEditorCore
40897  * @extends Roo.Component
40898  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40899  *
40900  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40901  */
40902
40903 Roo.HtmlEditorCore = function(config){
40904     
40905     
40906     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40907     this.addEvents({
40908         /**
40909          * @event initialize
40910          * Fires when the editor is fully initialized (including the iframe)
40911          * @param {Roo.HtmlEditorCore} this
40912          */
40913         initialize: true,
40914         /**
40915          * @event activate
40916          * Fires when the editor is first receives the focus. Any insertion must wait
40917          * until after this event.
40918          * @param {Roo.HtmlEditorCore} this
40919          */
40920         activate: true,
40921          /**
40922          * @event beforesync
40923          * Fires before the textarea is updated with content from the editor iframe. Return false
40924          * to cancel the sync.
40925          * @param {Roo.HtmlEditorCore} this
40926          * @param {String} html
40927          */
40928         beforesync: true,
40929          /**
40930          * @event beforepush
40931          * Fires before the iframe editor is updated with content from the textarea. Return false
40932          * to cancel the push.
40933          * @param {Roo.HtmlEditorCore} this
40934          * @param {String} html
40935          */
40936         beforepush: true,
40937          /**
40938          * @event sync
40939          * Fires when the textarea is updated with content from the editor iframe.
40940          * @param {Roo.HtmlEditorCore} this
40941          * @param {String} html
40942          */
40943         sync: true,
40944          /**
40945          * @event push
40946          * Fires when the iframe editor is updated with content from the textarea.
40947          * @param {Roo.HtmlEditorCore} this
40948          * @param {String} html
40949          */
40950         push: true,
40951         
40952         /**
40953          * @event editorevent
40954          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40955          * @param {Roo.HtmlEditorCore} this
40956          */
40957         editorevent: true
40958     });
40959      
40960 };
40961
40962
40963 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
40964
40965
40966      /**
40967      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
40968      */
40969     
40970     owner : false,
40971     
40972      /**
40973      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40974      *                        Roo.resizable.
40975      */
40976     resizable : false,
40977      /**
40978      * @cfg {Number} height (in pixels)
40979      */   
40980     height: 300,
40981    /**
40982      * @cfg {Number} width (in pixels)
40983      */   
40984     width: 500,
40985     
40986     /**
40987      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40988      * 
40989      */
40990     stylesheets: false,
40991     
40992     // id of frame..
40993     frameId: false,
40994     
40995     // private properties
40996     validationEvent : false,
40997     deferHeight: true,
40998     initialized : false,
40999     activated : false,
41000     sourceEditMode : false,
41001     onFocus : Roo.emptyFn,
41002     iframePad:3,
41003     hideMode:'offsets',
41004     
41005     clearUp: true,
41006     
41007      
41008     
41009
41010     /**
41011      * Protected method that will not generally be called directly. It
41012      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41013      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41014      */
41015     getDocMarkup : function(){
41016         // body styles..
41017         var st = '';
41018         Roo.log(this.stylesheets);
41019         
41020         // inherit styels from page...?? 
41021         if (this.stylesheets === false) {
41022             
41023             Roo.get(document.head).select('style').each(function(node) {
41024                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41025             });
41026             
41027             Roo.get(document.head).select('link').each(function(node) { 
41028                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41029             });
41030             
41031         } else if (!this.stylesheets.length) {
41032                 // simple..
41033                 st = '<style type="text/css">' +
41034                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41035                    '</style>';
41036         } else {
41037             Roo.each(this.stylesheets, function(s) {
41038                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41039             });
41040             
41041         }
41042         
41043         st +=  '<style type="text/css">' +
41044             'IMG { cursor: pointer } ' +
41045         '</style>';
41046
41047         
41048         return '<html><head>' + st  +
41049             //<style type="text/css">' +
41050             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41051             //'</style>' +
41052             ' </head><body class="roo-htmleditor-body"></body></html>';
41053     },
41054
41055     // private
41056     onRender : function(ct, position)
41057     {
41058         var _t = this;
41059         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41060         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41061         
41062         
41063         this.el.dom.style.border = '0 none';
41064         this.el.dom.setAttribute('tabIndex', -1);
41065         this.el.addClass('x-hidden hide');
41066         
41067         
41068         
41069         if(Roo.isIE){ // fix IE 1px bogus margin
41070             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41071         }
41072        
41073         
41074         this.frameId = Roo.id();
41075         
41076          
41077         
41078         var iframe = this.owner.wrap.createChild({
41079             tag: 'iframe',
41080             cls: 'form-control', // bootstrap..
41081             id: this.frameId,
41082             name: this.frameId,
41083             frameBorder : 'no',
41084             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41085         }, this.el
41086         );
41087         
41088         
41089         this.iframe = iframe.dom;
41090
41091          this.assignDocWin();
41092         
41093         this.doc.designMode = 'on';
41094        
41095         this.doc.open();
41096         this.doc.write(this.getDocMarkup());
41097         this.doc.close();
41098
41099         
41100         var task = { // must defer to wait for browser to be ready
41101             run : function(){
41102                 //console.log("run task?" + this.doc.readyState);
41103                 this.assignDocWin();
41104                 if(this.doc.body || this.doc.readyState == 'complete'){
41105                     try {
41106                         this.doc.designMode="on";
41107                     } catch (e) {
41108                         return;
41109                     }
41110                     Roo.TaskMgr.stop(task);
41111                     this.initEditor.defer(10, this);
41112                 }
41113             },
41114             interval : 10,
41115             duration: 10000,
41116             scope: this
41117         };
41118         Roo.TaskMgr.start(task);
41119
41120         
41121          
41122     },
41123
41124     // private
41125     onResize : function(w, h)
41126     {
41127          Roo.log('resize: ' +w + ',' + h );
41128         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41129         if(!this.iframe){
41130             return;
41131         }
41132         if(typeof w == 'number'){
41133             
41134             this.iframe.style.width = w + 'px';
41135         }
41136         if(typeof h == 'number'){
41137             
41138             this.iframe.style.height = h + 'px';
41139             if(this.doc){
41140                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41141             }
41142         }
41143         
41144     },
41145
41146     /**
41147      * Toggles the editor between standard and source edit mode.
41148      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41149      */
41150     toggleSourceEdit : function(sourceEditMode){
41151         
41152         this.sourceEditMode = sourceEditMode === true;
41153         
41154         if(this.sourceEditMode){
41155  
41156             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41157             
41158         }else{
41159             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41160             //this.iframe.className = '';
41161             this.deferFocus();
41162         }
41163         //this.setSize(this.owner.wrap.getSize());
41164         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41165     },
41166
41167     
41168   
41169
41170     /**
41171      * Protected method that will not generally be called directly. If you need/want
41172      * custom HTML cleanup, this is the method you should override.
41173      * @param {String} html The HTML to be cleaned
41174      * return {String} The cleaned HTML
41175      */
41176     cleanHtml : function(html){
41177         html = String(html);
41178         if(html.length > 5){
41179             if(Roo.isSafari){ // strip safari nonsense
41180                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41181             }
41182         }
41183         if(html == '&nbsp;'){
41184             html = '';
41185         }
41186         return html;
41187     },
41188
41189     /**
41190      * HTML Editor -> Textarea
41191      * Protected method that will not generally be called directly. Syncs the contents
41192      * of the editor iframe with the textarea.
41193      */
41194     syncValue : function(){
41195         if(this.initialized){
41196             var bd = (this.doc.body || this.doc.documentElement);
41197             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41198             var html = bd.innerHTML;
41199             if(Roo.isSafari){
41200                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41201                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41202                 if(m && m[1]){
41203                     html = '<div style="'+m[0]+'">' + html + '</div>';
41204                 }
41205             }
41206             html = this.cleanHtml(html);
41207             // fix up the special chars.. normaly like back quotes in word...
41208             // however we do not want to do this with chinese..
41209             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41210                 var cc = b.charCodeAt();
41211                 if (
41212                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41213                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41214                     (cc >= 0xf900 && cc < 0xfb00 )
41215                 ) {
41216                         return b;
41217                 }
41218                 return "&#"+cc+";" 
41219             });
41220             if(this.owner.fireEvent('beforesync', this, html) !== false){
41221                 this.el.dom.value = html;
41222                 this.owner.fireEvent('sync', this, html);
41223             }
41224         }
41225     },
41226
41227     /**
41228      * Protected method that will not generally be called directly. Pushes the value of the textarea
41229      * into the iframe editor.
41230      */
41231     pushValue : function(){
41232         if(this.initialized){
41233             var v = this.el.dom.value.trim();
41234             
41235 //            if(v.length < 1){
41236 //                v = '&#160;';
41237 //            }
41238             
41239             if(this.owner.fireEvent('beforepush', this, v) !== false){
41240                 var d = (this.doc.body || this.doc.documentElement);
41241                 d.innerHTML = v;
41242                 this.cleanUpPaste();
41243                 this.el.dom.value = d.innerHTML;
41244                 this.owner.fireEvent('push', this, v);
41245             }
41246         }
41247     },
41248
41249     // private
41250     deferFocus : function(){
41251         this.focus.defer(10, this);
41252     },
41253
41254     // doc'ed in Field
41255     focus : function(){
41256         if(this.win && !this.sourceEditMode){
41257             this.win.focus();
41258         }else{
41259             this.el.focus();
41260         }
41261     },
41262     
41263     assignDocWin: function()
41264     {
41265         var iframe = this.iframe;
41266         
41267          if(Roo.isIE){
41268             this.doc = iframe.contentWindow.document;
41269             this.win = iframe.contentWindow;
41270         } else {
41271             if (!Roo.get(this.frameId)) {
41272                 return;
41273             }
41274             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41275             this.win = Roo.get(this.frameId).dom.contentWindow;
41276         }
41277     },
41278     
41279     // private
41280     initEditor : function(){
41281         //console.log("INIT EDITOR");
41282         this.assignDocWin();
41283         
41284         
41285         
41286         this.doc.designMode="on";
41287         this.doc.open();
41288         this.doc.write(this.getDocMarkup());
41289         this.doc.close();
41290         
41291         var dbody = (this.doc.body || this.doc.documentElement);
41292         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41293         // this copies styles from the containing element into thsi one..
41294         // not sure why we need all of this..
41295         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41296         ss['background-attachment'] = 'fixed'; // w3c
41297         dbody.bgProperties = 'fixed'; // ie
41298         Roo.DomHelper.applyStyles(dbody, ss);
41299         Roo.EventManager.on(this.doc, {
41300             //'mousedown': this.onEditorEvent,
41301             'mouseup': this.onEditorEvent,
41302             'dblclick': this.onEditorEvent,
41303             'click': this.onEditorEvent,
41304             'keyup': this.onEditorEvent,
41305             buffer:100,
41306             scope: this
41307         });
41308         if(Roo.isGecko){
41309             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41310         }
41311         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41312             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41313         }
41314         this.initialized = true;
41315
41316         this.owner.fireEvent('initialize', this);
41317         this.pushValue();
41318     },
41319
41320     // private
41321     onDestroy : function(){
41322         
41323         
41324         
41325         if(this.rendered){
41326             
41327             //for (var i =0; i < this.toolbars.length;i++) {
41328             //    // fixme - ask toolbars for heights?
41329             //    this.toolbars[i].onDestroy();
41330            // }
41331             
41332             //this.wrap.dom.innerHTML = '';
41333             //this.wrap.remove();
41334         }
41335     },
41336
41337     // private
41338     onFirstFocus : function(){
41339         
41340         this.assignDocWin();
41341         
41342         
41343         this.activated = true;
41344          
41345     
41346         if(Roo.isGecko){ // prevent silly gecko errors
41347             this.win.focus();
41348             var s = this.win.getSelection();
41349             if(!s.focusNode || s.focusNode.nodeType != 3){
41350                 var r = s.getRangeAt(0);
41351                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41352                 r.collapse(true);
41353                 this.deferFocus();
41354             }
41355             try{
41356                 this.execCmd('useCSS', true);
41357                 this.execCmd('styleWithCSS', false);
41358             }catch(e){}
41359         }
41360         this.owner.fireEvent('activate', this);
41361     },
41362
41363     // private
41364     adjustFont: function(btn){
41365         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41366         //if(Roo.isSafari){ // safari
41367         //    adjust *= 2;
41368        // }
41369         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41370         if(Roo.isSafari){ // safari
41371             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41372             v =  (v < 10) ? 10 : v;
41373             v =  (v > 48) ? 48 : v;
41374             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41375             
41376         }
41377         
41378         
41379         v = Math.max(1, v+adjust);
41380         
41381         this.execCmd('FontSize', v  );
41382     },
41383
41384     onEditorEvent : function(e){
41385         this.owner.fireEvent('editorevent', this, e);
41386       //  this.updateToolbar();
41387         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41388     },
41389
41390     insertTag : function(tg)
41391     {
41392         // could be a bit smarter... -> wrap the current selected tRoo..
41393         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41394             
41395             range = this.createRange(this.getSelection());
41396             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41397             wrappingNode.appendChild(range.extractContents());
41398             range.insertNode(wrappingNode);
41399
41400             return;
41401             
41402             
41403             
41404         }
41405         this.execCmd("formatblock",   tg);
41406         
41407     },
41408     
41409     insertText : function(txt)
41410     {
41411         
41412         
41413         var range = this.createRange();
41414         range.deleteContents();
41415                //alert(Sender.getAttribute('label'));
41416                
41417         range.insertNode(this.doc.createTextNode(txt));
41418     } ,
41419     
41420      
41421
41422     /**
41423      * Executes a Midas editor command on the editor document and performs necessary focus and
41424      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41425      * @param {String} cmd The Midas command
41426      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41427      */
41428     relayCmd : function(cmd, value){
41429         this.win.focus();
41430         this.execCmd(cmd, value);
41431         this.owner.fireEvent('editorevent', this);
41432         //this.updateToolbar();
41433         this.owner.deferFocus();
41434     },
41435
41436     /**
41437      * Executes a Midas editor command directly on the editor document.
41438      * For visual commands, you should use {@link #relayCmd} instead.
41439      * <b>This should only be called after the editor is initialized.</b>
41440      * @param {String} cmd The Midas command
41441      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41442      */
41443     execCmd : function(cmd, value){
41444         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41445         this.syncValue();
41446     },
41447  
41448  
41449    
41450     /**
41451      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41452      * to insert tRoo.
41453      * @param {String} text | dom node.. 
41454      */
41455     insertAtCursor : function(text)
41456     {
41457         
41458         
41459         
41460         if(!this.activated){
41461             return;
41462         }
41463         /*
41464         if(Roo.isIE){
41465             this.win.focus();
41466             var r = this.doc.selection.createRange();
41467             if(r){
41468                 r.collapse(true);
41469                 r.pasteHTML(text);
41470                 this.syncValue();
41471                 this.deferFocus();
41472             
41473             }
41474             return;
41475         }
41476         */
41477         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41478             this.win.focus();
41479             
41480             
41481             // from jquery ui (MIT licenced)
41482             var range, node;
41483             var win = this.win;
41484             
41485             if (win.getSelection && win.getSelection().getRangeAt) {
41486                 range = win.getSelection().getRangeAt(0);
41487                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41488                 range.insertNode(node);
41489             } else if (win.document.selection && win.document.selection.createRange) {
41490                 // no firefox support
41491                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41492                 win.document.selection.createRange().pasteHTML(txt);
41493             } else {
41494                 // no firefox support
41495                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41496                 this.execCmd('InsertHTML', txt);
41497             } 
41498             
41499             this.syncValue();
41500             
41501             this.deferFocus();
41502         }
41503     },
41504  // private
41505     mozKeyPress : function(e){
41506         if(e.ctrlKey){
41507             var c = e.getCharCode(), cmd;
41508           
41509             if(c > 0){
41510                 c = String.fromCharCode(c).toLowerCase();
41511                 switch(c){
41512                     case 'b':
41513                         cmd = 'bold';
41514                         break;
41515                     case 'i':
41516                         cmd = 'italic';
41517                         break;
41518                     
41519                     case 'u':
41520                         cmd = 'underline';
41521                         break;
41522                     
41523                     case 'v':
41524                         this.cleanUpPaste.defer(100, this);
41525                         return;
41526                         
41527                 }
41528                 if(cmd){
41529                     this.win.focus();
41530                     this.execCmd(cmd);
41531                     this.deferFocus();
41532                     e.preventDefault();
41533                 }
41534                 
41535             }
41536         }
41537     },
41538
41539     // private
41540     fixKeys : function(){ // load time branching for fastest keydown performance
41541         if(Roo.isIE){
41542             return function(e){
41543                 var k = e.getKey(), r;
41544                 if(k == e.TAB){
41545                     e.stopEvent();
41546                     r = this.doc.selection.createRange();
41547                     if(r){
41548                         r.collapse(true);
41549                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41550                         this.deferFocus();
41551                     }
41552                     return;
41553                 }
41554                 
41555                 if(k == e.ENTER){
41556                     r = this.doc.selection.createRange();
41557                     if(r){
41558                         var target = r.parentElement();
41559                         if(!target || target.tagName.toLowerCase() != 'li'){
41560                             e.stopEvent();
41561                             r.pasteHTML('<br />');
41562                             r.collapse(false);
41563                             r.select();
41564                         }
41565                     }
41566                 }
41567                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41568                     this.cleanUpPaste.defer(100, this);
41569                     return;
41570                 }
41571                 
41572                 
41573             };
41574         }else if(Roo.isOpera){
41575             return function(e){
41576                 var k = e.getKey();
41577                 if(k == e.TAB){
41578                     e.stopEvent();
41579                     this.win.focus();
41580                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41581                     this.deferFocus();
41582                 }
41583                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41584                     this.cleanUpPaste.defer(100, this);
41585                     return;
41586                 }
41587                 
41588             };
41589         }else if(Roo.isSafari){
41590             return function(e){
41591                 var k = e.getKey();
41592                 
41593                 if(k == e.TAB){
41594                     e.stopEvent();
41595                     this.execCmd('InsertText','\t');
41596                     this.deferFocus();
41597                     return;
41598                 }
41599                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41600                     this.cleanUpPaste.defer(100, this);
41601                     return;
41602                 }
41603                 
41604              };
41605         }
41606     }(),
41607     
41608     getAllAncestors: function()
41609     {
41610         var p = this.getSelectedNode();
41611         var a = [];
41612         if (!p) {
41613             a.push(p); // push blank onto stack..
41614             p = this.getParentElement();
41615         }
41616         
41617         
41618         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41619             a.push(p);
41620             p = p.parentNode;
41621         }
41622         a.push(this.doc.body);
41623         return a;
41624     },
41625     lastSel : false,
41626     lastSelNode : false,
41627     
41628     
41629     getSelection : function() 
41630     {
41631         this.assignDocWin();
41632         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41633     },
41634     
41635     getSelectedNode: function() 
41636     {
41637         // this may only work on Gecko!!!
41638         
41639         // should we cache this!!!!
41640         
41641         
41642         
41643          
41644         var range = this.createRange(this.getSelection()).cloneRange();
41645         
41646         if (Roo.isIE) {
41647             var parent = range.parentElement();
41648             while (true) {
41649                 var testRange = range.duplicate();
41650                 testRange.moveToElementText(parent);
41651                 if (testRange.inRange(range)) {
41652                     break;
41653                 }
41654                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41655                     break;
41656                 }
41657                 parent = parent.parentElement;
41658             }
41659             return parent;
41660         }
41661         
41662         // is ancestor a text element.
41663         var ac =  range.commonAncestorContainer;
41664         if (ac.nodeType == 3) {
41665             ac = ac.parentNode;
41666         }
41667         
41668         var ar = ac.childNodes;
41669          
41670         var nodes = [];
41671         var other_nodes = [];
41672         var has_other_nodes = false;
41673         for (var i=0;i<ar.length;i++) {
41674             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41675                 continue;
41676             }
41677             // fullly contained node.
41678             
41679             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41680                 nodes.push(ar[i]);
41681                 continue;
41682             }
41683             
41684             // probably selected..
41685             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41686                 other_nodes.push(ar[i]);
41687                 continue;
41688             }
41689             // outer..
41690             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41691                 continue;
41692             }
41693             
41694             
41695             has_other_nodes = true;
41696         }
41697         if (!nodes.length && other_nodes.length) {
41698             nodes= other_nodes;
41699         }
41700         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41701             return false;
41702         }
41703         
41704         return nodes[0];
41705     },
41706     createRange: function(sel)
41707     {
41708         // this has strange effects when using with 
41709         // top toolbar - not sure if it's a great idea.
41710         //this.editor.contentWindow.focus();
41711         if (typeof sel != "undefined") {
41712             try {
41713                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41714             } catch(e) {
41715                 return this.doc.createRange();
41716             }
41717         } else {
41718             return this.doc.createRange();
41719         }
41720     },
41721     getParentElement: function()
41722     {
41723         
41724         this.assignDocWin();
41725         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41726         
41727         var range = this.createRange(sel);
41728          
41729         try {
41730             var p = range.commonAncestorContainer;
41731             while (p.nodeType == 3) { // text node
41732                 p = p.parentNode;
41733             }
41734             return p;
41735         } catch (e) {
41736             return null;
41737         }
41738     
41739     },
41740     /***
41741      *
41742      * Range intersection.. the hard stuff...
41743      *  '-1' = before
41744      *  '0' = hits..
41745      *  '1' = after.
41746      *         [ -- selected range --- ]
41747      *   [fail]                        [fail]
41748      *
41749      *    basically..
41750      *      if end is before start or  hits it. fail.
41751      *      if start is after end or hits it fail.
41752      *
41753      *   if either hits (but other is outside. - then it's not 
41754      *   
41755      *    
41756      **/
41757     
41758     
41759     // @see http://www.thismuchiknow.co.uk/?p=64.
41760     rangeIntersectsNode : function(range, node)
41761     {
41762         var nodeRange = node.ownerDocument.createRange();
41763         try {
41764             nodeRange.selectNode(node);
41765         } catch (e) {
41766             nodeRange.selectNodeContents(node);
41767         }
41768     
41769         var rangeStartRange = range.cloneRange();
41770         rangeStartRange.collapse(true);
41771     
41772         var rangeEndRange = range.cloneRange();
41773         rangeEndRange.collapse(false);
41774     
41775         var nodeStartRange = nodeRange.cloneRange();
41776         nodeStartRange.collapse(true);
41777     
41778         var nodeEndRange = nodeRange.cloneRange();
41779         nodeEndRange.collapse(false);
41780     
41781         return rangeStartRange.compareBoundaryPoints(
41782                  Range.START_TO_START, nodeEndRange) == -1 &&
41783                rangeEndRange.compareBoundaryPoints(
41784                  Range.START_TO_START, nodeStartRange) == 1;
41785         
41786          
41787     },
41788     rangeCompareNode : function(range, node)
41789     {
41790         var nodeRange = node.ownerDocument.createRange();
41791         try {
41792             nodeRange.selectNode(node);
41793         } catch (e) {
41794             nodeRange.selectNodeContents(node);
41795         }
41796         
41797         
41798         range.collapse(true);
41799     
41800         nodeRange.collapse(true);
41801      
41802         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41803         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41804          
41805         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41806         
41807         var nodeIsBefore   =  ss == 1;
41808         var nodeIsAfter    = ee == -1;
41809         
41810         if (nodeIsBefore && nodeIsAfter)
41811             return 0; // outer
41812         if (!nodeIsBefore && nodeIsAfter)
41813             return 1; //right trailed.
41814         
41815         if (nodeIsBefore && !nodeIsAfter)
41816             return 2;  // left trailed.
41817         // fully contined.
41818         return 3;
41819     },
41820
41821     // private? - in a new class?
41822     cleanUpPaste :  function()
41823     {
41824         // cleans up the whole document..
41825         Roo.log('cleanuppaste');
41826         
41827         this.cleanUpChildren(this.doc.body);
41828         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41829         if (clean != this.doc.body.innerHTML) {
41830             this.doc.body.innerHTML = clean;
41831         }
41832         
41833     },
41834     
41835     cleanWordChars : function(input) {// change the chars to hex code
41836         var he = Roo.HtmlEditorCore;
41837         
41838         var output = input;
41839         Roo.each(he.swapCodes, function(sw) { 
41840             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41841             
41842             output = output.replace(swapper, sw[1]);
41843         });
41844         
41845         return output;
41846     },
41847     
41848     
41849     cleanUpChildren : function (n)
41850     {
41851         if (!n.childNodes.length) {
41852             return;
41853         }
41854         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41855            this.cleanUpChild(n.childNodes[i]);
41856         }
41857     },
41858     
41859     
41860         
41861     
41862     cleanUpChild : function (node)
41863     {
41864         var ed = this;
41865         //console.log(node);
41866         if (node.nodeName == "#text") {
41867             // clean up silly Windows -- stuff?
41868             return; 
41869         }
41870         if (node.nodeName == "#comment") {
41871             node.parentNode.removeChild(node);
41872             // clean up silly Windows -- stuff?
41873             return; 
41874         }
41875         
41876         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
41877             // remove node.
41878             node.parentNode.removeChild(node);
41879             return;
41880             
41881         }
41882         
41883         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41884         
41885         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41886         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41887         
41888         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41889         //    remove_keep_children = true;
41890         //}
41891         
41892         if (remove_keep_children) {
41893             this.cleanUpChildren(node);
41894             // inserts everything just before this node...
41895             while (node.childNodes.length) {
41896                 var cn = node.childNodes[0];
41897                 node.removeChild(cn);
41898                 node.parentNode.insertBefore(cn, node);
41899             }
41900             node.parentNode.removeChild(node);
41901             return;
41902         }
41903         
41904         if (!node.attributes || !node.attributes.length) {
41905             this.cleanUpChildren(node);
41906             return;
41907         }
41908         
41909         function cleanAttr(n,v)
41910         {
41911             
41912             if (v.match(/^\./) || v.match(/^\//)) {
41913                 return;
41914             }
41915             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41916                 return;
41917             }
41918             if (v.match(/^#/)) {
41919                 return;
41920             }
41921 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41922             node.removeAttribute(n);
41923             
41924         }
41925         
41926         function cleanStyle(n,v)
41927         {
41928             if (v.match(/expression/)) { //XSS?? should we even bother..
41929                 node.removeAttribute(n);
41930                 return;
41931             }
41932             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
41933             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
41934             
41935             
41936             var parts = v.split(/;/);
41937             var clean = [];
41938             
41939             Roo.each(parts, function(p) {
41940                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41941                 if (!p.length) {
41942                     return true;
41943                 }
41944                 var l = p.split(':').shift().replace(/\s+/g,'');
41945                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41946                 
41947                 if ( cblack.indexOf(l) > -1) {
41948 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41949                     //node.removeAttribute(n);
41950                     return true;
41951                 }
41952                 //Roo.log()
41953                 // only allow 'c whitelisted system attributes'
41954                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41955 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41956                     //node.removeAttribute(n);
41957                     return true;
41958                 }
41959                 
41960                 
41961                  
41962                 
41963                 clean.push(p);
41964                 return true;
41965             });
41966             if (clean.length) { 
41967                 node.setAttribute(n, clean.join(';'));
41968             } else {
41969                 node.removeAttribute(n);
41970             }
41971             
41972         }
41973         
41974         
41975         for (var i = node.attributes.length-1; i > -1 ; i--) {
41976             var a = node.attributes[i];
41977             //console.log(a);
41978             
41979             if (a.name.toLowerCase().substr(0,2)=='on')  {
41980                 node.removeAttribute(a.name);
41981                 continue;
41982             }
41983             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
41984                 node.removeAttribute(a.name);
41985                 continue;
41986             }
41987             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
41988                 cleanAttr(a.name,a.value); // fixme..
41989                 continue;
41990             }
41991             if (a.name == 'style') {
41992                 cleanStyle(a.name,a.value);
41993                 continue;
41994             }
41995             /// clean up MS crap..
41996             // tecnically this should be a list of valid class'es..
41997             
41998             
41999             if (a.name == 'class') {
42000                 if (a.value.match(/^Mso/)) {
42001                     node.className = '';
42002                 }
42003                 
42004                 if (a.value.match(/body/)) {
42005                     node.className = '';
42006                 }
42007                 continue;
42008             }
42009             
42010             // style cleanup!?
42011             // class cleanup?
42012             
42013         }
42014         
42015         
42016         this.cleanUpChildren(node);
42017         
42018         
42019     },
42020     /**
42021      * Clean up MS wordisms...
42022      */
42023     cleanWord : function(node)
42024     {
42025         var _t = this;
42026         var cleanWordChildren = function()
42027         {
42028             if (!node.childNodes.length) {
42029                 return;
42030             }
42031             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42032                _t.cleanWord(node.childNodes[i]);
42033             }
42034         }
42035         
42036         
42037         if (!node) {
42038             this.cleanWord(this.doc.body);
42039             return;
42040         }
42041         if (node.nodeName == "#text") {
42042             // clean up silly Windows -- stuff?
42043             return; 
42044         }
42045         if (node.nodeName == "#comment") {
42046             node.parentNode.removeChild(node);
42047             // clean up silly Windows -- stuff?
42048             return; 
42049         }
42050         
42051         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42052             node.parentNode.removeChild(node);
42053             return;
42054         }
42055         
42056         // remove - but keep children..
42057         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42058             while (node.childNodes.length) {
42059                 var cn = node.childNodes[0];
42060                 node.removeChild(cn);
42061                 node.parentNode.insertBefore(cn, node);
42062             }
42063             node.parentNode.removeChild(node);
42064             cleanWordChildren();
42065             return;
42066         }
42067         // clean styles
42068         if (node.className.length) {
42069             
42070             var cn = node.className.split(/\W+/);
42071             var cna = [];
42072             Roo.each(cn, function(cls) {
42073                 if (cls.match(/Mso[a-zA-Z]+/)) {
42074                     return;
42075                 }
42076                 cna.push(cls);
42077             });
42078             node.className = cna.length ? cna.join(' ') : '';
42079             if (!cna.length) {
42080                 node.removeAttribute("class");
42081             }
42082         }
42083         
42084         if (node.hasAttribute("lang")) {
42085             node.removeAttribute("lang");
42086         }
42087         
42088         if (node.hasAttribute("style")) {
42089             
42090             var styles = node.getAttribute("style").split(";");
42091             var nstyle = [];
42092             Roo.each(styles, function(s) {
42093                 if (!s.match(/:/)) {
42094                     return;
42095                 }
42096                 var kv = s.split(":");
42097                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42098                     return;
42099                 }
42100                 // what ever is left... we allow.
42101                 nstyle.push(s);
42102             });
42103             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42104             if (!nstyle.length) {
42105                 node.removeAttribute('style');
42106             }
42107         }
42108         
42109         cleanWordChildren();
42110         
42111         
42112     },
42113     domToHTML : function(currentElement, depth, nopadtext) {
42114         
42115             depth = depth || 0;
42116             nopadtext = nopadtext || false;
42117         
42118             if (!currentElement) {
42119                 return this.domToHTML(this.doc.body);
42120             }
42121             
42122             //Roo.log(currentElement);
42123             var j;
42124             var allText = false;
42125             var nodeName = currentElement.nodeName;
42126             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42127             
42128             if  (nodeName == '#text') {
42129                 return currentElement.nodeValue;
42130             }
42131             
42132             
42133             var ret = '';
42134             if (nodeName != 'BODY') {
42135                  
42136                 var i = 0;
42137                 // Prints the node tagName, such as <A>, <IMG>, etc
42138                 if (tagName) {
42139                     var attr = [];
42140                     for(i = 0; i < currentElement.attributes.length;i++) {
42141                         // quoting?
42142                         var aname = currentElement.attributes.item(i).name;
42143                         if (!currentElement.attributes.item(i).value.length) {
42144                             continue;
42145                         }
42146                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42147                     }
42148                     
42149                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42150                 } 
42151                 else {
42152                     
42153                     // eack
42154                 }
42155             } else {
42156                 tagName = false;
42157             }
42158             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42159                 return ret;
42160             }
42161             if (['PRE', 'TEXTAREA', 'TD', 'A'].indexOf(tagName) > -1) { // or code?
42162                 nopadtext = true;
42163             }
42164             
42165             
42166             // Traverse the tree
42167             i = 0;
42168             var currentElementChild = currentElement.childNodes.item(i);
42169             var allText = true;
42170             var innerHTML  = '';
42171             while (currentElementChild) {
42172                 // Formatting code (indent the tree so it looks nice on the screen)
42173                 
42174                 if  (currentElementChild.nodeName == '#text') {
42175                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42176                     if (!nopadtext && toadd.length > 80) {
42177                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42178                     }
42179                     innerHTML  += toadd;
42180                     
42181                     i++;
42182                     currentElementChild = currentElement.childNodes.item(i);
42183                     continue;
42184                 }   
42185                 allText = false;
42186                 innerHTML  += nopadtext ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42187                     
42188                 // Recursively traverse the tree structure of the child node
42189                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42190                 i++;
42191                 currentElementChild=currentElement.childNodes.item(i);
42192             }
42193             
42194             ret += innerHTML;
42195             
42196             if (!allText) {
42197                     // The remaining code is mostly for formatting the tree
42198                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42199             }
42200             
42201             
42202             if (tagName) {
42203                 ret+= "</"+tagName+">";
42204             }
42205             return ret;
42206             
42207         }
42208     
42209     // hide stuff that is not compatible
42210     /**
42211      * @event blur
42212      * @hide
42213      */
42214     /**
42215      * @event change
42216      * @hide
42217      */
42218     /**
42219      * @event focus
42220      * @hide
42221      */
42222     /**
42223      * @event specialkey
42224      * @hide
42225      */
42226     /**
42227      * @cfg {String} fieldClass @hide
42228      */
42229     /**
42230      * @cfg {String} focusClass @hide
42231      */
42232     /**
42233      * @cfg {String} autoCreate @hide
42234      */
42235     /**
42236      * @cfg {String} inputType @hide
42237      */
42238     /**
42239      * @cfg {String} invalidClass @hide
42240      */
42241     /**
42242      * @cfg {String} invalidText @hide
42243      */
42244     /**
42245      * @cfg {String} msgFx @hide
42246      */
42247     /**
42248      * @cfg {String} validateOnBlur @hide
42249      */
42250 });
42251
42252 Roo.HtmlEditorCore.white = [
42253         'area', 'br', 'img', 'input', 'hr', 'wbr',
42254         
42255        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42256        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42257        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42258        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42259        'table',   'ul',         'xmp', 
42260        
42261        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42262       'thead',   'tr', 
42263      
42264       'dir', 'menu', 'ol', 'ul', 'dl',
42265        
42266       'embed',  'object'
42267 ];
42268
42269
42270 Roo.HtmlEditorCore.black = [
42271     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42272         'applet', // 
42273         'base',   'basefont', 'bgsound', 'blink',  'body', 
42274         'frame',  'frameset', 'head',    'html',   'ilayer', 
42275         'iframe', 'layer',  'link',     'meta',    'object',   
42276         'script', 'style' ,'title',  'xml' // clean later..
42277 ];
42278 Roo.HtmlEditorCore.clean = [
42279     'script', 'style', 'title', 'xml'
42280 ];
42281 Roo.HtmlEditorCore.remove = [
42282     'font'
42283 ];
42284 // attributes..
42285
42286 Roo.HtmlEditorCore.ablack = [
42287     'on'
42288 ];
42289     
42290 Roo.HtmlEditorCore.aclean = [ 
42291     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42292 ];
42293
42294 // protocols..
42295 Roo.HtmlEditorCore.pwhite= [
42296         'http',  'https',  'mailto'
42297 ];
42298
42299 // white listed style attributes.
42300 Roo.HtmlEditorCore.cwhite= [
42301       //  'text-align', /// default is to allow most things..
42302       
42303          
42304 //        'font-size'//??
42305 ];
42306
42307 // black listed style attributes.
42308 Roo.HtmlEditorCore.cblack= [
42309       //  'font-size' -- this can be set by the project 
42310 ];
42311
42312
42313 Roo.HtmlEditorCore.swapCodes   =[ 
42314     [    8211, "--" ], 
42315     [    8212, "--" ], 
42316     [    8216,  "'" ],  
42317     [    8217, "'" ],  
42318     [    8220, '"' ],  
42319     [    8221, '"' ],  
42320     [    8226, "*" ],  
42321     [    8230, "..." ]
42322 ]; 
42323
42324     //<script type="text/javascript">
42325
42326 /*
42327  * Ext JS Library 1.1.1
42328  * Copyright(c) 2006-2007, Ext JS, LLC.
42329  * Licence LGPL
42330  * 
42331  */
42332  
42333  
42334 Roo.form.HtmlEditor = function(config){
42335     
42336     
42337     
42338     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42339     
42340     if (!this.toolbars) {
42341         this.toolbars = [];
42342     }
42343     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42344     
42345     
42346 };
42347
42348 /**
42349  * @class Roo.form.HtmlEditor
42350  * @extends Roo.form.Field
42351  * Provides a lightweight HTML Editor component.
42352  *
42353  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42354  * 
42355  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42356  * supported by this editor.</b><br/><br/>
42357  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42358  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42359  */
42360 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42361     /**
42362      * @cfg {Boolean} clearUp
42363      */
42364     clearUp : true,
42365       /**
42366      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42367      */
42368     toolbars : false,
42369    
42370      /**
42371      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42372      *                        Roo.resizable.
42373      */
42374     resizable : false,
42375      /**
42376      * @cfg {Number} height (in pixels)
42377      */   
42378     height: 300,
42379    /**
42380      * @cfg {Number} width (in pixels)
42381      */   
42382     width: 500,
42383     
42384     /**
42385      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42386      * 
42387      */
42388     stylesheets: false,
42389     
42390     // id of frame..
42391     frameId: false,
42392     
42393     // private properties
42394     validationEvent : false,
42395     deferHeight: true,
42396     initialized : false,
42397     activated : false,
42398     
42399     onFocus : Roo.emptyFn,
42400     iframePad:3,
42401     hideMode:'offsets',
42402     
42403     defaultAutoCreate : { // modified by initCompnoent..
42404         tag: "textarea",
42405         style:"width:500px;height:300px;",
42406         autocomplete: "off"
42407     },
42408
42409     // private
42410     initComponent : function(){
42411         this.addEvents({
42412             /**
42413              * @event initialize
42414              * Fires when the editor is fully initialized (including the iframe)
42415              * @param {HtmlEditor} this
42416              */
42417             initialize: true,
42418             /**
42419              * @event activate
42420              * Fires when the editor is first receives the focus. Any insertion must wait
42421              * until after this event.
42422              * @param {HtmlEditor} this
42423              */
42424             activate: true,
42425              /**
42426              * @event beforesync
42427              * Fires before the textarea is updated with content from the editor iframe. Return false
42428              * to cancel the sync.
42429              * @param {HtmlEditor} this
42430              * @param {String} html
42431              */
42432             beforesync: true,
42433              /**
42434              * @event beforepush
42435              * Fires before the iframe editor is updated with content from the textarea. Return false
42436              * to cancel the push.
42437              * @param {HtmlEditor} this
42438              * @param {String} html
42439              */
42440             beforepush: true,
42441              /**
42442              * @event sync
42443              * Fires when the textarea is updated with content from the editor iframe.
42444              * @param {HtmlEditor} this
42445              * @param {String} html
42446              */
42447             sync: true,
42448              /**
42449              * @event push
42450              * Fires when the iframe editor is updated with content from the textarea.
42451              * @param {HtmlEditor} this
42452              * @param {String} html
42453              */
42454             push: true,
42455              /**
42456              * @event editmodechange
42457              * Fires when the editor switches edit modes
42458              * @param {HtmlEditor} this
42459              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42460              */
42461             editmodechange: true,
42462             /**
42463              * @event editorevent
42464              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42465              * @param {HtmlEditor} this
42466              */
42467             editorevent: true,
42468             /**
42469              * @event firstfocus
42470              * Fires when on first focus - needed by toolbars..
42471              * @param {HtmlEditor} this
42472              */
42473             firstfocus: true,
42474             /**
42475              * @event autosave
42476              * Auto save the htmlEditor value as a file into Events
42477              * @param {HtmlEditor} this
42478              */
42479             autosave: true,
42480             /**
42481              * @event savedpreview
42482              * preview the saved version of htmlEditor
42483              * @param {HtmlEditor} this
42484              */
42485             savedpreview: true
42486         });
42487         this.defaultAutoCreate =  {
42488             tag: "textarea",
42489             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42490             autocomplete: "off"
42491         };
42492     },
42493
42494     /**
42495      * Protected method that will not generally be called directly. It
42496      * is called when the editor creates its toolbar. Override this method if you need to
42497      * add custom toolbar buttons.
42498      * @param {HtmlEditor} editor
42499      */
42500     createToolbar : function(editor){
42501         Roo.log("create toolbars");
42502         if (!editor.toolbars || !editor.toolbars.length) {
42503             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42504         }
42505         
42506         for (var i =0 ; i < editor.toolbars.length;i++) {
42507             editor.toolbars[i] = Roo.factory(
42508                     typeof(editor.toolbars[i]) == 'string' ?
42509                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42510                 Roo.form.HtmlEditor);
42511             editor.toolbars[i].init(editor);
42512         }
42513          
42514         
42515     },
42516
42517      
42518     // private
42519     onRender : function(ct, position)
42520     {
42521         var _t = this;
42522         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42523         
42524         this.wrap = this.el.wrap({
42525             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42526         });
42527         
42528         this.editorcore.onRender(ct, position);
42529          
42530         if (this.resizable) {
42531             this.resizeEl = new Roo.Resizable(this.wrap, {
42532                 pinned : true,
42533                 wrap: true,
42534                 dynamic : true,
42535                 minHeight : this.height,
42536                 height: this.height,
42537                 handles : this.resizable,
42538                 width: this.width,
42539                 listeners : {
42540                     resize : function(r, w, h) {
42541                         _t.onResize(w,h); // -something
42542                     }
42543                 }
42544             });
42545             
42546         }
42547         this.createToolbar(this);
42548        
42549         
42550         if(!this.width){
42551             this.setSize(this.wrap.getSize());
42552         }
42553         if (this.resizeEl) {
42554             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42555             // should trigger onReize..
42556         }
42557         
42558 //        if(this.autosave && this.w){
42559 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42560 //        }
42561     },
42562
42563     // private
42564     onResize : function(w, h)
42565     {
42566         //Roo.log('resize: ' +w + ',' + h );
42567         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42568         var ew = false;
42569         var eh = false;
42570         
42571         if(this.el ){
42572             if(typeof w == 'number'){
42573                 var aw = w - this.wrap.getFrameWidth('lr');
42574                 this.el.setWidth(this.adjustWidth('textarea', aw));
42575                 ew = aw;
42576             }
42577             if(typeof h == 'number'){
42578                 var tbh = 0;
42579                 for (var i =0; i < this.toolbars.length;i++) {
42580                     // fixme - ask toolbars for heights?
42581                     tbh += this.toolbars[i].tb.el.getHeight();
42582                     if (this.toolbars[i].footer) {
42583                         tbh += this.toolbars[i].footer.el.getHeight();
42584                     }
42585                 }
42586                 
42587                 
42588                 
42589                 
42590                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42591                 ah -= 5; // knock a few pixes off for look..
42592                 this.el.setHeight(this.adjustWidth('textarea', ah));
42593                 var eh = ah;
42594             }
42595         }
42596         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42597         this.editorcore.onResize(ew,eh);
42598         
42599     },
42600
42601     /**
42602      * Toggles the editor between standard and source edit mode.
42603      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42604      */
42605     toggleSourceEdit : function(sourceEditMode)
42606     {
42607         this.editorcore.toggleSourceEdit(sourceEditMode);
42608         
42609         if(this.editorcore.sourceEditMode){
42610             Roo.log('editor - showing textarea');
42611             
42612 //            Roo.log('in');
42613 //            Roo.log(this.syncValue());
42614             this.editorcore.syncValue();
42615             this.el.removeClass('x-hidden');
42616             this.el.dom.removeAttribute('tabIndex');
42617             this.el.focus();
42618         }else{
42619             Roo.log('editor - hiding textarea');
42620 //            Roo.log('out')
42621 //            Roo.log(this.pushValue()); 
42622             this.editorcore.pushValue();
42623             
42624             this.el.addClass('x-hidden');
42625             this.el.dom.setAttribute('tabIndex', -1);
42626             //this.deferFocus();
42627         }
42628          
42629         this.setSize(this.wrap.getSize());
42630         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42631     },
42632  
42633     // private (for BoxComponent)
42634     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42635
42636     // private (for BoxComponent)
42637     getResizeEl : function(){
42638         return this.wrap;
42639     },
42640
42641     // private (for BoxComponent)
42642     getPositionEl : function(){
42643         return this.wrap;
42644     },
42645
42646     // private
42647     initEvents : function(){
42648         this.originalValue = this.getValue();
42649     },
42650
42651     /**
42652      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42653      * @method
42654      */
42655     markInvalid : Roo.emptyFn,
42656     /**
42657      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42658      * @method
42659      */
42660     clearInvalid : Roo.emptyFn,
42661
42662     setValue : function(v){
42663         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42664         this.editorcore.pushValue();
42665     },
42666
42667      
42668     // private
42669     deferFocus : function(){
42670         this.focus.defer(10, this);
42671     },
42672
42673     // doc'ed in Field
42674     focus : function(){
42675         this.editorcore.focus();
42676         
42677     },
42678       
42679
42680     // private
42681     onDestroy : function(){
42682         
42683         
42684         
42685         if(this.rendered){
42686             
42687             for (var i =0; i < this.toolbars.length;i++) {
42688                 // fixme - ask toolbars for heights?
42689                 this.toolbars[i].onDestroy();
42690             }
42691             
42692             this.wrap.dom.innerHTML = '';
42693             this.wrap.remove();
42694         }
42695     },
42696
42697     // private
42698     onFirstFocus : function(){
42699         //Roo.log("onFirstFocus");
42700         this.editorcore.onFirstFocus();
42701          for (var i =0; i < this.toolbars.length;i++) {
42702             this.toolbars[i].onFirstFocus();
42703         }
42704         
42705     },
42706     
42707     // private
42708     syncValue : function()
42709     {
42710         this.editorcore.syncValue();
42711     },
42712     
42713     pushValue : function()
42714     {
42715         this.editorcore.pushValue();
42716     }
42717      
42718     
42719     // hide stuff that is not compatible
42720     /**
42721      * @event blur
42722      * @hide
42723      */
42724     /**
42725      * @event change
42726      * @hide
42727      */
42728     /**
42729      * @event focus
42730      * @hide
42731      */
42732     /**
42733      * @event specialkey
42734      * @hide
42735      */
42736     /**
42737      * @cfg {String} fieldClass @hide
42738      */
42739     /**
42740      * @cfg {String} focusClass @hide
42741      */
42742     /**
42743      * @cfg {String} autoCreate @hide
42744      */
42745     /**
42746      * @cfg {String} inputType @hide
42747      */
42748     /**
42749      * @cfg {String} invalidClass @hide
42750      */
42751     /**
42752      * @cfg {String} invalidText @hide
42753      */
42754     /**
42755      * @cfg {String} msgFx @hide
42756      */
42757     /**
42758      * @cfg {String} validateOnBlur @hide
42759      */
42760 });
42761  
42762     // <script type="text/javascript">
42763 /*
42764  * Based on
42765  * Ext JS Library 1.1.1
42766  * Copyright(c) 2006-2007, Ext JS, LLC.
42767  *  
42768  
42769  */
42770
42771 /**
42772  * @class Roo.form.HtmlEditorToolbar1
42773  * Basic Toolbar
42774  * 
42775  * Usage:
42776  *
42777  new Roo.form.HtmlEditor({
42778     ....
42779     toolbars : [
42780         new Roo.form.HtmlEditorToolbar1({
42781             disable : { fonts: 1 , format: 1, ..., ... , ...],
42782             btns : [ .... ]
42783         })
42784     }
42785      
42786  * 
42787  * @cfg {Object} disable List of elements to disable..
42788  * @cfg {Array} btns List of additional buttons.
42789  * 
42790  * 
42791  * NEEDS Extra CSS? 
42792  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42793  */
42794  
42795 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42796 {
42797     
42798     Roo.apply(this, config);
42799     
42800     // default disabled, based on 'good practice'..
42801     this.disable = this.disable || {};
42802     Roo.applyIf(this.disable, {
42803         fontSize : true,
42804         colors : true,
42805         specialElements : true
42806     });
42807     
42808     
42809     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42810     // dont call parent... till later.
42811 }
42812
42813 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42814     
42815     tb: false,
42816     
42817     rendered: false,
42818     
42819     editor : false,
42820     editorcore : false,
42821     /**
42822      * @cfg {Object} disable  List of toolbar elements to disable
42823          
42824      */
42825     disable : false,
42826     
42827     
42828      /**
42829      * @cfg {String} createLinkText The default text for the create link prompt
42830      */
42831     createLinkText : 'Please enter the URL for the link:',
42832     /**
42833      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42834      */
42835     defaultLinkValue : 'http:/'+'/',
42836    
42837     
42838       /**
42839      * @cfg {Array} fontFamilies An array of available font families
42840      */
42841     fontFamilies : [
42842         'Arial',
42843         'Courier New',
42844         'Tahoma',
42845         'Times New Roman',
42846         'Verdana'
42847     ],
42848     
42849     specialChars : [
42850            "&#169;",
42851           "&#174;",     
42852           "&#8482;",    
42853           "&#163;" ,    
42854          // "&#8212;",    
42855           "&#8230;",    
42856           "&#247;" ,    
42857         //  "&#225;" ,     ?? a acute?
42858            "&#8364;"    , //Euro
42859        //   "&#8220;"    ,
42860         //  "&#8221;"    ,
42861         //  "&#8226;"    ,
42862           "&#176;"  //   , // degrees
42863
42864          // "&#233;"     , // e ecute
42865          // "&#250;"     , // u ecute?
42866     ],
42867     
42868     specialElements : [
42869         {
42870             text: "Insert Table",
42871             xtype: 'MenuItem',
42872             xns : Roo.Menu,
42873             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42874                 
42875         },
42876         {    
42877             text: "Insert Image",
42878             xtype: 'MenuItem',
42879             xns : Roo.Menu,
42880             ihtml : '<img src="about:blank"/>'
42881             
42882         }
42883         
42884          
42885     ],
42886     
42887     
42888     inputElements : [ 
42889             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42890             "input:submit", "input:button", "select", "textarea", "label" ],
42891     formats : [
42892         ["p"] ,  
42893         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42894         ["pre"],[ "code"], 
42895         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42896         ['div'],['span']
42897     ],
42898     
42899     cleanStyles : [
42900         "font-size"
42901     ],
42902      /**
42903      * @cfg {String} defaultFont default font to use.
42904      */
42905     defaultFont: 'tahoma',
42906    
42907     fontSelect : false,
42908     
42909     
42910     formatCombo : false,
42911     
42912     init : function(editor)
42913     {
42914         this.editor = editor;
42915         this.editorcore = editor.editorcore ? editor.editorcore : editor;
42916         var editorcore = this.editorcore;
42917         
42918         var _t = this;
42919         
42920         var fid = editorcore.frameId;
42921         var etb = this;
42922         function btn(id, toggle, handler){
42923             var xid = fid + '-'+ id ;
42924             return {
42925                 id : xid,
42926                 cmd : id,
42927                 cls : 'x-btn-icon x-edit-'+id,
42928                 enableToggle:toggle !== false,
42929                 scope: _t, // was editor...
42930                 handler:handler||_t.relayBtnCmd,
42931                 clickEvent:'mousedown',
42932                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42933                 tabIndex:-1
42934             };
42935         }
42936         
42937         
42938         
42939         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42940         this.tb = tb;
42941          // stop form submits
42942         tb.el.on('click', function(e){
42943             e.preventDefault(); // what does this do?
42944         });
42945
42946         if(!this.disable.font) { // && !Roo.isSafari){
42947             /* why no safari for fonts 
42948             editor.fontSelect = tb.el.createChild({
42949                 tag:'select',
42950                 tabIndex: -1,
42951                 cls:'x-font-select',
42952                 html: this.createFontOptions()
42953             });
42954             
42955             editor.fontSelect.on('change', function(){
42956                 var font = editor.fontSelect.dom.value;
42957                 editor.relayCmd('fontname', font);
42958                 editor.deferFocus();
42959             }, editor);
42960             
42961             tb.add(
42962                 editor.fontSelect.dom,
42963                 '-'
42964             );
42965             */
42966             
42967         };
42968         if(!this.disable.formats){
42969             this.formatCombo = new Roo.form.ComboBox({
42970                 store: new Roo.data.SimpleStore({
42971                     id : 'tag',
42972                     fields: ['tag'],
42973                     data : this.formats // from states.js
42974                 }),
42975                 blockFocus : true,
42976                 name : '',
42977                 //autoCreate : {tag: "div",  size: "20"},
42978                 displayField:'tag',
42979                 typeAhead: false,
42980                 mode: 'local',
42981                 editable : false,
42982                 triggerAction: 'all',
42983                 emptyText:'Add tag',
42984                 selectOnFocus:true,
42985                 width:135,
42986                 listeners : {
42987                     'select': function(c, r, i) {
42988                         editorcore.insertTag(r.get('tag'));
42989                         editor.focus();
42990                     }
42991                 }
42992
42993             });
42994             tb.addField(this.formatCombo);
42995             
42996         }
42997         
42998         if(!this.disable.format){
42999             tb.add(
43000                 btn('bold'),
43001                 btn('italic'),
43002                 btn('underline')
43003             );
43004         };
43005         if(!this.disable.fontSize){
43006             tb.add(
43007                 '-',
43008                 
43009                 
43010                 btn('increasefontsize', false, editorcore.adjustFont),
43011                 btn('decreasefontsize', false, editorcore.adjustFont)
43012             );
43013         };
43014         
43015         
43016         if(!this.disable.colors){
43017             tb.add(
43018                 '-', {
43019                     id:editorcore.frameId +'-forecolor',
43020                     cls:'x-btn-icon x-edit-forecolor',
43021                     clickEvent:'mousedown',
43022                     tooltip: this.buttonTips['forecolor'] || undefined,
43023                     tabIndex:-1,
43024                     menu : new Roo.menu.ColorMenu({
43025                         allowReselect: true,
43026                         focus: Roo.emptyFn,
43027                         value:'000000',
43028                         plain:true,
43029                         selectHandler: function(cp, color){
43030                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43031                             editor.deferFocus();
43032                         },
43033                         scope: editorcore,
43034                         clickEvent:'mousedown'
43035                     })
43036                 }, {
43037                     id:editorcore.frameId +'backcolor',
43038                     cls:'x-btn-icon x-edit-backcolor',
43039                     clickEvent:'mousedown',
43040                     tooltip: this.buttonTips['backcolor'] || undefined,
43041                     tabIndex:-1,
43042                     menu : new Roo.menu.ColorMenu({
43043                         focus: Roo.emptyFn,
43044                         value:'FFFFFF',
43045                         plain:true,
43046                         allowReselect: true,
43047                         selectHandler: function(cp, color){
43048                             if(Roo.isGecko){
43049                                 editorcore.execCmd('useCSS', false);
43050                                 editorcore.execCmd('hilitecolor', color);
43051                                 editorcore.execCmd('useCSS', true);
43052                                 editor.deferFocus();
43053                             }else{
43054                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43055                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43056                                 editor.deferFocus();
43057                             }
43058                         },
43059                         scope:editorcore,
43060                         clickEvent:'mousedown'
43061                     })
43062                 }
43063             );
43064         };
43065         // now add all the items...
43066         
43067
43068         if(!this.disable.alignments){
43069             tb.add(
43070                 '-',
43071                 btn('justifyleft'),
43072                 btn('justifycenter'),
43073                 btn('justifyright')
43074             );
43075         };
43076
43077         //if(!Roo.isSafari){
43078             if(!this.disable.links){
43079                 tb.add(
43080                     '-',
43081                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43082                 );
43083             };
43084
43085             if(!this.disable.lists){
43086                 tb.add(
43087                     '-',
43088                     btn('insertorderedlist'),
43089                     btn('insertunorderedlist')
43090                 );
43091             }
43092             if(!this.disable.sourceEdit){
43093                 tb.add(
43094                     '-',
43095                     btn('sourceedit', true, function(btn){
43096                         Roo.log(this);
43097                         this.toggleSourceEdit(btn.pressed);
43098                     })
43099                 );
43100             }
43101         //}
43102         
43103         var smenu = { };
43104         // special menu.. - needs to be tidied up..
43105         if (!this.disable.special) {
43106             smenu = {
43107                 text: "&#169;",
43108                 cls: 'x-edit-none',
43109                 
43110                 menu : {
43111                     items : []
43112                 }
43113             };
43114             for (var i =0; i < this.specialChars.length; i++) {
43115                 smenu.menu.items.push({
43116                     
43117                     html: this.specialChars[i],
43118                     handler: function(a,b) {
43119                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43120                         //editor.insertAtCursor(a.html);
43121                         
43122                     },
43123                     tabIndex:-1
43124                 });
43125             }
43126             
43127             
43128             tb.add(smenu);
43129             
43130             
43131         }
43132         
43133         var cmenu = { };
43134         if (!this.disable.cleanStyles) {
43135             cmenu = {
43136                 cls: 'x-btn-icon x-btn-clear',
43137                 
43138                 menu : {
43139                     items : []
43140                 }
43141             };
43142             for (var i =0; i < this.cleanStyles.length; i++) {
43143                 cmenu.menu.items.push({
43144                     actiontype : this.cleanStyles[i],
43145                     html: 'Remove ' + this.cleanStyles[i],
43146                     handler: function(a,b) {
43147                         Roo.log(a);
43148                         Roo.log(b);
43149                         var c = Roo.get(editorcore.doc.body);
43150                         c.select('[style]').each(function(s) {
43151                             s.dom.style.removeProperty(a.actiontype);
43152                         });
43153                         
43154                     },
43155                     tabIndex:-1
43156                 });
43157             }
43158             cmenu.menu.items.push({
43159                 actiontype : 'word',
43160                 html: 'Remove MS Word Formating',
43161                 handler: function(a,b) {
43162                     editorcore.cleanWord();
43163                     
43164                 },
43165                 tabIndex:-1
43166             });
43167             
43168             cmenu.menu.items.push({
43169                 actiontype : 'all',
43170                 html: 'Remove All Styles',
43171                 handler: function(a,b) {
43172                     
43173                     var c = Roo.get(editorcore.doc.body);
43174                     c.select('[style]').each(function(s) {
43175                         s.dom.removeAttribute('style');
43176                     });
43177                     
43178                 },
43179                 tabIndex:-1
43180             });
43181              cmenu.menu.items.push({
43182                 actiontype : 'word',
43183                 html: 'Tidy HTML Source',
43184                 handler: function(a,b) {
43185                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43186                     
43187                 },
43188                 tabIndex:-1
43189             });
43190             
43191             
43192             tb.add(cmenu);
43193         }
43194          
43195         if (!this.disable.specialElements) {
43196             var semenu = {
43197                 text: "Other;",
43198                 cls: 'x-edit-none',
43199                 menu : {
43200                     items : []
43201                 }
43202             };
43203             for (var i =0; i < this.specialElements.length; i++) {
43204                 semenu.menu.items.push(
43205                     Roo.apply({ 
43206                         handler: function(a,b) {
43207                             editor.insertAtCursor(this.ihtml);
43208                         }
43209                     }, this.specialElements[i])
43210                 );
43211                     
43212             }
43213             
43214             tb.add(semenu);
43215             
43216             
43217         }
43218          
43219         
43220         if (this.btns) {
43221             for(var i =0; i< this.btns.length;i++) {
43222                 var b = Roo.factory(this.btns[i],Roo.form);
43223                 b.cls =  'x-edit-none';
43224                 b.scope = editorcore;
43225                 tb.add(b);
43226             }
43227         
43228         }
43229         
43230         
43231         
43232         // disable everything...
43233         
43234         this.tb.items.each(function(item){
43235            if(item.id != editorcore.frameId+ '-sourceedit'){
43236                 item.disable();
43237             }
43238         });
43239         this.rendered = true;
43240         
43241         // the all the btns;
43242         editor.on('editorevent', this.updateToolbar, this);
43243         // other toolbars need to implement this..
43244         //editor.on('editmodechange', this.updateToolbar, this);
43245     },
43246     
43247     
43248     relayBtnCmd : function(btn) {
43249         this.editorcore.relayCmd(btn.cmd);
43250     },
43251     // private used internally
43252     createLink : function(){
43253         Roo.log("create link?");
43254         var url = prompt(this.createLinkText, this.defaultLinkValue);
43255         if(url && url != 'http:/'+'/'){
43256             this.editorcore.relayCmd('createlink', url);
43257         }
43258     },
43259
43260     
43261     /**
43262      * Protected method that will not generally be called directly. It triggers
43263      * a toolbar update by reading the markup state of the current selection in the editor.
43264      */
43265     updateToolbar: function(){
43266
43267         if(!this.editorcore.activated){
43268             this.editor.onFirstFocus();
43269             return;
43270         }
43271
43272         var btns = this.tb.items.map, 
43273             doc = this.editorcore.doc,
43274             frameId = this.editorcore.frameId;
43275
43276         if(!this.disable.font && !Roo.isSafari){
43277             /*
43278             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43279             if(name != this.fontSelect.dom.value){
43280                 this.fontSelect.dom.value = name;
43281             }
43282             */
43283         }
43284         if(!this.disable.format){
43285             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43286             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43287             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43288         }
43289         if(!this.disable.alignments){
43290             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43291             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43292             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43293         }
43294         if(!Roo.isSafari && !this.disable.lists){
43295             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43296             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43297         }
43298         
43299         var ans = this.editorcore.getAllAncestors();
43300         if (this.formatCombo) {
43301             
43302             
43303             var store = this.formatCombo.store;
43304             this.formatCombo.setValue("");
43305             for (var i =0; i < ans.length;i++) {
43306                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43307                     // select it..
43308                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43309                     break;
43310                 }
43311             }
43312         }
43313         
43314         
43315         
43316         // hides menus... - so this cant be on a menu...
43317         Roo.menu.MenuMgr.hideAll();
43318
43319         //this.editorsyncValue();
43320     },
43321    
43322     
43323     createFontOptions : function(){
43324         var buf = [], fs = this.fontFamilies, ff, lc;
43325         
43326         
43327         
43328         for(var i = 0, len = fs.length; i< len; i++){
43329             ff = fs[i];
43330             lc = ff.toLowerCase();
43331             buf.push(
43332                 '<option value="',lc,'" style="font-family:',ff,';"',
43333                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43334                     ff,
43335                 '</option>'
43336             );
43337         }
43338         return buf.join('');
43339     },
43340     
43341     toggleSourceEdit : function(sourceEditMode){
43342         
43343         Roo.log("toolbar toogle");
43344         if(sourceEditMode === undefined){
43345             sourceEditMode = !this.sourceEditMode;
43346         }
43347         this.sourceEditMode = sourceEditMode === true;
43348         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43349         // just toggle the button?
43350         if(btn.pressed !== this.sourceEditMode){
43351             btn.toggle(this.sourceEditMode);
43352             return;
43353         }
43354         
43355         if(sourceEditMode){
43356             Roo.log("disabling buttons");
43357             this.tb.items.each(function(item){
43358                 if(item.cmd != 'sourceedit'){
43359                     item.disable();
43360                 }
43361             });
43362           
43363         }else{
43364             Roo.log("enabling buttons");
43365             if(this.editorcore.initialized){
43366                 this.tb.items.each(function(item){
43367                     item.enable();
43368                 });
43369             }
43370             
43371         }
43372         Roo.log("calling toggole on editor");
43373         // tell the editor that it's been pressed..
43374         this.editor.toggleSourceEdit(sourceEditMode);
43375        
43376     },
43377      /**
43378      * Object collection of toolbar tooltips for the buttons in the editor. The key
43379      * is the command id associated with that button and the value is a valid QuickTips object.
43380      * For example:
43381 <pre><code>
43382 {
43383     bold : {
43384         title: 'Bold (Ctrl+B)',
43385         text: 'Make the selected text bold.',
43386         cls: 'x-html-editor-tip'
43387     },
43388     italic : {
43389         title: 'Italic (Ctrl+I)',
43390         text: 'Make the selected text italic.',
43391         cls: 'x-html-editor-tip'
43392     },
43393     ...
43394 </code></pre>
43395     * @type Object
43396      */
43397     buttonTips : {
43398         bold : {
43399             title: 'Bold (Ctrl+B)',
43400             text: 'Make the selected text bold.',
43401             cls: 'x-html-editor-tip'
43402         },
43403         italic : {
43404             title: 'Italic (Ctrl+I)',
43405             text: 'Make the selected text italic.',
43406             cls: 'x-html-editor-tip'
43407         },
43408         underline : {
43409             title: 'Underline (Ctrl+U)',
43410             text: 'Underline the selected text.',
43411             cls: 'x-html-editor-tip'
43412         },
43413         increasefontsize : {
43414             title: 'Grow Text',
43415             text: 'Increase the font size.',
43416             cls: 'x-html-editor-tip'
43417         },
43418         decreasefontsize : {
43419             title: 'Shrink Text',
43420             text: 'Decrease the font size.',
43421             cls: 'x-html-editor-tip'
43422         },
43423         backcolor : {
43424             title: 'Text Highlight Color',
43425             text: 'Change the background color of the selected text.',
43426             cls: 'x-html-editor-tip'
43427         },
43428         forecolor : {
43429             title: 'Font Color',
43430             text: 'Change the color of the selected text.',
43431             cls: 'x-html-editor-tip'
43432         },
43433         justifyleft : {
43434             title: 'Align Text Left',
43435             text: 'Align text to the left.',
43436             cls: 'x-html-editor-tip'
43437         },
43438         justifycenter : {
43439             title: 'Center Text',
43440             text: 'Center text in the editor.',
43441             cls: 'x-html-editor-tip'
43442         },
43443         justifyright : {
43444             title: 'Align Text Right',
43445             text: 'Align text to the right.',
43446             cls: 'x-html-editor-tip'
43447         },
43448         insertunorderedlist : {
43449             title: 'Bullet List',
43450             text: 'Start a bulleted list.',
43451             cls: 'x-html-editor-tip'
43452         },
43453         insertorderedlist : {
43454             title: 'Numbered List',
43455             text: 'Start a numbered list.',
43456             cls: 'x-html-editor-tip'
43457         },
43458         createlink : {
43459             title: 'Hyperlink',
43460             text: 'Make the selected text a hyperlink.',
43461             cls: 'x-html-editor-tip'
43462         },
43463         sourceedit : {
43464             title: 'Source Edit',
43465             text: 'Switch to source editing mode.',
43466             cls: 'x-html-editor-tip'
43467         }
43468     },
43469     // private
43470     onDestroy : function(){
43471         if(this.rendered){
43472             
43473             this.tb.items.each(function(item){
43474                 if(item.menu){
43475                     item.menu.removeAll();
43476                     if(item.menu.el){
43477                         item.menu.el.destroy();
43478                     }
43479                 }
43480                 item.destroy();
43481             });
43482              
43483         }
43484     },
43485     onFirstFocus: function() {
43486         this.tb.items.each(function(item){
43487            item.enable();
43488         });
43489     }
43490 });
43491
43492
43493
43494
43495 // <script type="text/javascript">
43496 /*
43497  * Based on
43498  * Ext JS Library 1.1.1
43499  * Copyright(c) 2006-2007, Ext JS, LLC.
43500  *  
43501  
43502  */
43503
43504  
43505 /**
43506  * @class Roo.form.HtmlEditor.ToolbarContext
43507  * Context Toolbar
43508  * 
43509  * Usage:
43510  *
43511  new Roo.form.HtmlEditor({
43512     ....
43513     toolbars : [
43514         { xtype: 'ToolbarStandard', styles : {} }
43515         { xtype: 'ToolbarContext', disable : {} }
43516     ]
43517 })
43518
43519      
43520  * 
43521  * @config : {Object} disable List of elements to disable.. (not done yet.)
43522  * @config : {Object} styles  Map of styles available.
43523  * 
43524  */
43525
43526 Roo.form.HtmlEditor.ToolbarContext = function(config)
43527 {
43528     
43529     Roo.apply(this, config);
43530     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43531     // dont call parent... till later.
43532     this.styles = this.styles || {};
43533 }
43534
43535  
43536
43537 Roo.form.HtmlEditor.ToolbarContext.types = {
43538     'IMG' : {
43539         width : {
43540             title: "Width",
43541             width: 40
43542         },
43543         height:  {
43544             title: "Height",
43545             width: 40
43546         },
43547         align: {
43548             title: "Align",
43549             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43550             width : 80
43551             
43552         },
43553         border: {
43554             title: "Border",
43555             width: 40
43556         },
43557         alt: {
43558             title: "Alt",
43559             width: 120
43560         },
43561         src : {
43562             title: "Src",
43563             width: 220
43564         }
43565         
43566     },
43567     'A' : {
43568         name : {
43569             title: "Name",
43570             width: 50
43571         },
43572         target:  {
43573             title: "Target",
43574             width: 120
43575         },
43576         href:  {
43577             title: "Href",
43578             width: 220
43579         } // border?
43580         
43581     },
43582     'TABLE' : {
43583         rows : {
43584             title: "Rows",
43585             width: 20
43586         },
43587         cols : {
43588             title: "Cols",
43589             width: 20
43590         },
43591         width : {
43592             title: "Width",
43593             width: 40
43594         },
43595         height : {
43596             title: "Height",
43597             width: 40
43598         },
43599         border : {
43600             title: "Border",
43601             width: 20
43602         }
43603     },
43604     'TD' : {
43605         width : {
43606             title: "Width",
43607             width: 40
43608         },
43609         height : {
43610             title: "Height",
43611             width: 40
43612         },   
43613         align: {
43614             title: "Align",
43615             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43616             width: 80
43617         },
43618         valign: {
43619             title: "Valign",
43620             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43621             width: 80
43622         },
43623         colspan: {
43624             title: "Colspan",
43625             width: 20
43626             
43627         },
43628          'font-family'  : {
43629             title : "Font",
43630             style : 'fontFamily',
43631             displayField: 'display',
43632             optname : 'font-family',
43633             width: 140
43634         }
43635     },
43636     'INPUT' : {
43637         name : {
43638             title: "name",
43639             width: 120
43640         },
43641         value : {
43642             title: "Value",
43643             width: 120
43644         },
43645         width : {
43646             title: "Width",
43647             width: 40
43648         }
43649     },
43650     'LABEL' : {
43651         'for' : {
43652             title: "For",
43653             width: 120
43654         }
43655     },
43656     'TEXTAREA' : {
43657           name : {
43658             title: "name",
43659             width: 120
43660         },
43661         rows : {
43662             title: "Rows",
43663             width: 20
43664         },
43665         cols : {
43666             title: "Cols",
43667             width: 20
43668         }
43669     },
43670     'SELECT' : {
43671         name : {
43672             title: "name",
43673             width: 120
43674         },
43675         selectoptions : {
43676             title: "Options",
43677             width: 200
43678         }
43679     },
43680     
43681     // should we really allow this??
43682     // should this just be 
43683     'BODY' : {
43684         title : {
43685             title: "Title",
43686             width: 200,
43687             disabled : true
43688         }
43689     },
43690     'SPAN' : {
43691         'font-family'  : {
43692             title : "Font",
43693             style : 'fontFamily',
43694             displayField: 'display',
43695             optname : 'font-family',
43696             width: 140
43697         }
43698     },
43699     'DIV' : {
43700         'font-family'  : {
43701             title : "Font",
43702             style : 'fontFamily',
43703             displayField: 'display',
43704             optname : 'font-family',
43705             width: 140
43706         }
43707     },
43708      'P' : {
43709         'font-family'  : {
43710             title : "Font",
43711             style : 'fontFamily',
43712             displayField: 'display',
43713             optname : 'font-family',
43714             width: 140
43715         }
43716     },
43717     
43718     '*' : {
43719         // empty..
43720     }
43721
43722 };
43723
43724 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43725 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43726
43727 Roo.form.HtmlEditor.ToolbarContext.options = {
43728         'font-family'  : [ 
43729                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43730                 [ 'Courier New', 'Courier New'],
43731                 [ 'Tahoma', 'Tahoma'],
43732                 [ 'Times New Roman,serif', 'Times'],
43733                 [ 'Verdana','Verdana' ]
43734         ]
43735 };
43736
43737 // fixme - these need to be configurable..
43738  
43739
43740 Roo.form.HtmlEditor.ToolbarContext.types
43741
43742
43743 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43744     
43745     tb: false,
43746     
43747     rendered: false,
43748     
43749     editor : false,
43750     editorcore : false,
43751     /**
43752      * @cfg {Object} disable  List of toolbar elements to disable
43753          
43754      */
43755     disable : false,
43756     /**
43757      * @cfg {Object} styles List of styles 
43758      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43759      *
43760      * These must be defined in the page, so they get rendered correctly..
43761      * .headline { }
43762      * TD.underline { }
43763      * 
43764      */
43765     styles : false,
43766     
43767     options: false,
43768     
43769     toolbars : false,
43770     
43771     init : function(editor)
43772     {
43773         this.editor = editor;
43774         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43775         var editorcore = this.editorcore;
43776         
43777         var fid = editorcore.frameId;
43778         var etb = this;
43779         function btn(id, toggle, handler){
43780             var xid = fid + '-'+ id ;
43781             return {
43782                 id : xid,
43783                 cmd : id,
43784                 cls : 'x-btn-icon x-edit-'+id,
43785                 enableToggle:toggle !== false,
43786                 scope: editorcore, // was editor...
43787                 handler:handler||editorcore.relayBtnCmd,
43788                 clickEvent:'mousedown',
43789                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43790                 tabIndex:-1
43791             };
43792         }
43793         // create a new element.
43794         var wdiv = editor.wrap.createChild({
43795                 tag: 'div'
43796             }, editor.wrap.dom.firstChild.nextSibling, true);
43797         
43798         // can we do this more than once??
43799         
43800          // stop form submits
43801       
43802  
43803         // disable everything...
43804         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43805         this.toolbars = {};
43806            
43807         for (var i in  ty) {
43808           
43809             this.toolbars[i] = this.buildToolbar(ty[i],i);
43810         }
43811         this.tb = this.toolbars.BODY;
43812         this.tb.el.show();
43813         this.buildFooter();
43814         this.footer.show();
43815         editor.on('hide', function( ) { this.footer.hide() }, this);
43816         editor.on('show', function( ) { this.footer.show() }, this);
43817         
43818          
43819         this.rendered = true;
43820         
43821         // the all the btns;
43822         editor.on('editorevent', this.updateToolbar, this);
43823         // other toolbars need to implement this..
43824         //editor.on('editmodechange', this.updateToolbar, this);
43825     },
43826     
43827     
43828     
43829     /**
43830      * Protected method that will not generally be called directly. It triggers
43831      * a toolbar update by reading the markup state of the current selection in the editor.
43832      */
43833     updateToolbar: function(editor,ev,sel){
43834
43835         //Roo.log(ev);
43836         // capture mouse up - this is handy for selecting images..
43837         // perhaps should go somewhere else...
43838         if(!this.editorcore.activated){
43839              this.editor.onFirstFocus();
43840             return;
43841         }
43842         
43843         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43844         // selectNode - might want to handle IE?
43845         if (ev &&
43846             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43847             ev.target && ev.target.tagName == 'IMG') {
43848             // they have click on an image...
43849             // let's see if we can change the selection...
43850             sel = ev.target;
43851          
43852               var nodeRange = sel.ownerDocument.createRange();
43853             try {
43854                 nodeRange.selectNode(sel);
43855             } catch (e) {
43856                 nodeRange.selectNodeContents(sel);
43857             }
43858             //nodeRange.collapse(true);
43859             var s = this.editorcore.win.getSelection();
43860             s.removeAllRanges();
43861             s.addRange(nodeRange);
43862         }  
43863         
43864       
43865         var updateFooter = sel ? false : true;
43866         
43867         
43868         var ans = this.editorcore.getAllAncestors();
43869         
43870         // pick
43871         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43872         
43873         if (!sel) { 
43874             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43875             sel = sel ? sel : this.editorcore.doc.body;
43876             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43877             
43878         }
43879         // pick a menu that exists..
43880         var tn = sel.tagName.toUpperCase();
43881         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43882         
43883         tn = sel.tagName.toUpperCase();
43884         
43885         var lastSel = this.tb.selectedNode
43886         
43887         this.tb.selectedNode = sel;
43888         
43889         // if current menu does not match..
43890         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43891                 
43892             this.tb.el.hide();
43893             ///console.log("show: " + tn);
43894             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43895             this.tb.el.show();
43896             // update name
43897             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43898             
43899             
43900             // update attributes
43901             if (this.tb.fields) {
43902                 this.tb.fields.each(function(e) {
43903                     if (e.stylename) {
43904                         e.setValue(sel.style[e.stylename]);
43905                         return;
43906                     } 
43907                    e.setValue(sel.getAttribute(e.attrname));
43908                 });
43909             }
43910             
43911             var hasStyles = false;
43912             for(var i in this.styles) {
43913                 hasStyles = true;
43914                 break;
43915             }
43916             
43917             // update styles
43918             if (hasStyles) { 
43919                 var st = this.tb.fields.item(0);
43920                 
43921                 st.store.removeAll();
43922                
43923                 
43924                 var cn = sel.className.split(/\s+/);
43925                 
43926                 var avs = [];
43927                 if (this.styles['*']) {
43928                     
43929                     Roo.each(this.styles['*'], function(v) {
43930                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43931                     });
43932                 }
43933                 if (this.styles[tn]) { 
43934                     Roo.each(this.styles[tn], function(v) {
43935                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43936                     });
43937                 }
43938                 
43939                 st.store.loadData(avs);
43940                 st.collapse();
43941                 st.setValue(cn);
43942             }
43943             // flag our selected Node.
43944             this.tb.selectedNode = sel;
43945            
43946            
43947             Roo.menu.MenuMgr.hideAll();
43948
43949         }
43950         
43951         if (!updateFooter) {
43952             //this.footDisp.dom.innerHTML = ''; 
43953             return;
43954         }
43955         // update the footer
43956         //
43957         var html = '';
43958         
43959         this.footerEls = ans.reverse();
43960         Roo.each(this.footerEls, function(a,i) {
43961             if (!a) { return; }
43962             html += html.length ? ' &gt; '  :  '';
43963             
43964             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43965             
43966         });
43967        
43968         // 
43969         var sz = this.footDisp.up('td').getSize();
43970         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43971         this.footDisp.dom.style.marginLeft = '5px';
43972         
43973         this.footDisp.dom.style.overflow = 'hidden';
43974         
43975         this.footDisp.dom.innerHTML = html;
43976             
43977         //this.editorsyncValue();
43978     },
43979      
43980     
43981    
43982        
43983     // private
43984     onDestroy : function(){
43985         if(this.rendered){
43986             
43987             this.tb.items.each(function(item){
43988                 if(item.menu){
43989                     item.menu.removeAll();
43990                     if(item.menu.el){
43991                         item.menu.el.destroy();
43992                     }
43993                 }
43994                 item.destroy();
43995             });
43996              
43997         }
43998     },
43999     onFirstFocus: function() {
44000         // need to do this for all the toolbars..
44001         this.tb.items.each(function(item){
44002            item.enable();
44003         });
44004     },
44005     buildToolbar: function(tlist, nm)
44006     {
44007         var editor = this.editor;
44008         var editorcore = this.editorcore;
44009          // create a new element.
44010         var wdiv = editor.wrap.createChild({
44011                 tag: 'div'
44012             }, editor.wrap.dom.firstChild.nextSibling, true);
44013         
44014        
44015         var tb = new Roo.Toolbar(wdiv);
44016         // add the name..
44017         
44018         tb.add(nm+ ":&nbsp;");
44019         
44020         var styles = [];
44021         for(var i in this.styles) {
44022             styles.push(i);
44023         }
44024         
44025         // styles...
44026         if (styles && styles.length) {
44027             
44028             // this needs a multi-select checkbox...
44029             tb.addField( new Roo.form.ComboBox({
44030                 store: new Roo.data.SimpleStore({
44031                     id : 'val',
44032                     fields: ['val', 'selected'],
44033                     data : [] 
44034                 }),
44035                 name : '-roo-edit-className',
44036                 attrname : 'className',
44037                 displayField: 'val',
44038                 typeAhead: false,
44039                 mode: 'local',
44040                 editable : false,
44041                 triggerAction: 'all',
44042                 emptyText:'Select Style',
44043                 selectOnFocus:true,
44044                 width: 130,
44045                 listeners : {
44046                     'select': function(c, r, i) {
44047                         // initial support only for on class per el..
44048                         tb.selectedNode.className =  r ? r.get('val') : '';
44049                         editorcore.syncValue();
44050                     }
44051                 }
44052     
44053             }));
44054         }
44055         
44056         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44057         var tbops = tbc.options;
44058         
44059         for (var i in tlist) {
44060             
44061             var item = tlist[i];
44062             tb.add(item.title + ":&nbsp;");
44063             
44064             
44065             //optname == used so you can configure the options available..
44066             var opts = item.opts ? item.opts : false;
44067             if (item.optname) {
44068                 opts = tbops[item.optname];
44069            
44070             }
44071             
44072             if (opts) {
44073                 // opts == pulldown..
44074                 tb.addField( new Roo.form.ComboBox({
44075                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44076                         id : 'val',
44077                         fields: ['val', 'display'],
44078                         data : opts  
44079                     }),
44080                     name : '-roo-edit-' + i,
44081                     attrname : i,
44082                     stylename : item.style ? item.style : false,
44083                     displayField: item.displayField ? item.displayField : 'val',
44084                     valueField :  'val',
44085                     typeAhead: false,
44086                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44087                     editable : false,
44088                     triggerAction: 'all',
44089                     emptyText:'Select',
44090                     selectOnFocus:true,
44091                     width: item.width ? item.width  : 130,
44092                     listeners : {
44093                         'select': function(c, r, i) {
44094                             if (c.stylename) {
44095                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44096                                 return;
44097                             }
44098                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44099                         }
44100                     }
44101
44102                 }));
44103                 continue;
44104                     
44105                  
44106                 
44107                 tb.addField( new Roo.form.TextField({
44108                     name: i,
44109                     width: 100,
44110                     //allowBlank:false,
44111                     value: ''
44112                 }));
44113                 continue;
44114             }
44115             tb.addField( new Roo.form.TextField({
44116                 name: '-roo-edit-' + i,
44117                 attrname : i,
44118                 
44119                 width: item.width,
44120                 //allowBlank:true,
44121                 value: '',
44122                 listeners: {
44123                     'change' : function(f, nv, ov) {
44124                         tb.selectedNode.setAttribute(f.attrname, nv);
44125                     }
44126                 }
44127             }));
44128              
44129         }
44130         tb.addFill();
44131         var _this = this;
44132         tb.addButton( {
44133             text: 'Remove Tag',
44134     
44135             listeners : {
44136                 click : function ()
44137                 {
44138                     // remove
44139                     // undo does not work.
44140                      
44141                     var sn = tb.selectedNode;
44142                     
44143                     var pn = sn.parentNode;
44144                     
44145                     var stn =  sn.childNodes[0];
44146                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44147                     while (sn.childNodes.length) {
44148                         var node = sn.childNodes[0];
44149                         sn.removeChild(node);
44150                         //Roo.log(node);
44151                         pn.insertBefore(node, sn);
44152                         
44153                     }
44154                     pn.removeChild(sn);
44155                     var range = editorcore.createRange();
44156         
44157                     range.setStart(stn,0);
44158                     range.setEnd(en,0); //????
44159                     //range.selectNode(sel);
44160                     
44161                     
44162                     var selection = editorcore.getSelection();
44163                     selection.removeAllRanges();
44164                     selection.addRange(range);
44165                     
44166                     
44167                     
44168                     //_this.updateToolbar(null, null, pn);
44169                     _this.updateToolbar(null, null, null);
44170                     _this.footDisp.dom.innerHTML = ''; 
44171                 }
44172             }
44173             
44174                     
44175                 
44176             
44177         });
44178         
44179         
44180         tb.el.on('click', function(e){
44181             e.preventDefault(); // what does this do?
44182         });
44183         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44184         tb.el.hide();
44185         tb.name = nm;
44186         // dont need to disable them... as they will get hidden
44187         return tb;
44188          
44189         
44190     },
44191     buildFooter : function()
44192     {
44193         
44194         var fel = this.editor.wrap.createChild();
44195         this.footer = new Roo.Toolbar(fel);
44196         // toolbar has scrolly on left / right?
44197         var footDisp= new Roo.Toolbar.Fill();
44198         var _t = this;
44199         this.footer.add(
44200             {
44201                 text : '&lt;',
44202                 xtype: 'Button',
44203                 handler : function() {
44204                     _t.footDisp.scrollTo('left',0,true)
44205                 }
44206             }
44207         );
44208         this.footer.add( footDisp );
44209         this.footer.add( 
44210             {
44211                 text : '&gt;',
44212                 xtype: 'Button',
44213                 handler : function() {
44214                     // no animation..
44215                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44216                 }
44217             }
44218         );
44219         var fel = Roo.get(footDisp.el);
44220         fel.addClass('x-editor-context');
44221         this.footDispWrap = fel; 
44222         this.footDispWrap.overflow  = 'hidden';
44223         
44224         this.footDisp = fel.createChild();
44225         this.footDispWrap.on('click', this.onContextClick, this)
44226         
44227         
44228     },
44229     onContextClick : function (ev,dom)
44230     {
44231         ev.preventDefault();
44232         var  cn = dom.className;
44233         //Roo.log(cn);
44234         if (!cn.match(/x-ed-loc-/)) {
44235             return;
44236         }
44237         var n = cn.split('-').pop();
44238         var ans = this.footerEls;
44239         var sel = ans[n];
44240         
44241          // pick
44242         var range = this.editorcore.createRange();
44243         
44244         range.selectNodeContents(sel);
44245         //range.selectNode(sel);
44246         
44247         
44248         var selection = this.editorcore.getSelection();
44249         selection.removeAllRanges();
44250         selection.addRange(range);
44251         
44252         
44253         
44254         this.updateToolbar(null, null, sel);
44255         
44256         
44257     }
44258     
44259     
44260     
44261     
44262     
44263 });
44264
44265
44266
44267
44268
44269 /*
44270  * Based on:
44271  * Ext JS Library 1.1.1
44272  * Copyright(c) 2006-2007, Ext JS, LLC.
44273  *
44274  * Originally Released Under LGPL - original licence link has changed is not relivant.
44275  *
44276  * Fork - LGPL
44277  * <script type="text/javascript">
44278  */
44279  
44280 /**
44281  * @class Roo.form.BasicForm
44282  * @extends Roo.util.Observable
44283  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44284  * @constructor
44285  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44286  * @param {Object} config Configuration options
44287  */
44288 Roo.form.BasicForm = function(el, config){
44289     this.allItems = [];
44290     this.childForms = [];
44291     Roo.apply(this, config);
44292     /*
44293      * The Roo.form.Field items in this form.
44294      * @type MixedCollection
44295      */
44296      
44297      
44298     this.items = new Roo.util.MixedCollection(false, function(o){
44299         return o.id || (o.id = Roo.id());
44300     });
44301     this.addEvents({
44302         /**
44303          * @event beforeaction
44304          * Fires before any action is performed. Return false to cancel the action.
44305          * @param {Form} this
44306          * @param {Action} action The action to be performed
44307          */
44308         beforeaction: true,
44309         /**
44310          * @event actionfailed
44311          * Fires when an action fails.
44312          * @param {Form} this
44313          * @param {Action} action The action that failed
44314          */
44315         actionfailed : true,
44316         /**
44317          * @event actioncomplete
44318          * Fires when an action is completed.
44319          * @param {Form} this
44320          * @param {Action} action The action that completed
44321          */
44322         actioncomplete : true
44323     });
44324     if(el){
44325         this.initEl(el);
44326     }
44327     Roo.form.BasicForm.superclass.constructor.call(this);
44328 };
44329
44330 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44331     /**
44332      * @cfg {String} method
44333      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44334      */
44335     /**
44336      * @cfg {DataReader} reader
44337      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44338      * This is optional as there is built-in support for processing JSON.
44339      */
44340     /**
44341      * @cfg {DataReader} errorReader
44342      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44343      * This is completely optional as there is built-in support for processing JSON.
44344      */
44345     /**
44346      * @cfg {String} url
44347      * The URL to use for form actions if one isn't supplied in the action options.
44348      */
44349     /**
44350      * @cfg {Boolean} fileUpload
44351      * Set to true if this form is a file upload.
44352      */
44353      
44354     /**
44355      * @cfg {Object} baseParams
44356      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44357      */
44358      /**
44359      
44360     /**
44361      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44362      */
44363     timeout: 30,
44364
44365     // private
44366     activeAction : null,
44367
44368     /**
44369      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44370      * or setValues() data instead of when the form was first created.
44371      */
44372     trackResetOnLoad : false,
44373     
44374     
44375     /**
44376      * childForms - used for multi-tab forms
44377      * @type {Array}
44378      */
44379     childForms : false,
44380     
44381     /**
44382      * allItems - full list of fields.
44383      * @type {Array}
44384      */
44385     allItems : false,
44386     
44387     /**
44388      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44389      * element by passing it or its id or mask the form itself by passing in true.
44390      * @type Mixed
44391      */
44392     waitMsgTarget : false,
44393
44394     // private
44395     initEl : function(el){
44396         this.el = Roo.get(el);
44397         this.id = this.el.id || Roo.id();
44398         this.el.on('submit', this.onSubmit, this);
44399         this.el.addClass('x-form');
44400     },
44401
44402     // private
44403     onSubmit : function(e){
44404         e.stopEvent();
44405     },
44406
44407     /**
44408      * Returns true if client-side validation on the form is successful.
44409      * @return Boolean
44410      */
44411     isValid : function(){
44412         var valid = true;
44413         this.items.each(function(f){
44414            if(!f.validate()){
44415                valid = false;
44416            }
44417         });
44418         return valid;
44419     },
44420
44421     /**
44422      * Returns true if any fields in this form have changed since their original load.
44423      * @return Boolean
44424      */
44425     isDirty : function(){
44426         var dirty = false;
44427         this.items.each(function(f){
44428            if(f.isDirty()){
44429                dirty = true;
44430                return false;
44431            }
44432         });
44433         return dirty;
44434     },
44435
44436     /**
44437      * Performs a predefined action (submit or load) or custom actions you define on this form.
44438      * @param {String} actionName The name of the action type
44439      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44440      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44441      * accept other config options):
44442      * <pre>
44443 Property          Type             Description
44444 ----------------  ---------------  ----------------------------------------------------------------------------------
44445 url               String           The url for the action (defaults to the form's url)
44446 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44447 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44448 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44449                                    validate the form on the client (defaults to false)
44450      * </pre>
44451      * @return {BasicForm} this
44452      */
44453     doAction : function(action, options){
44454         if(typeof action == 'string'){
44455             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44456         }
44457         if(this.fireEvent('beforeaction', this, action) !== false){
44458             this.beforeAction(action);
44459             action.run.defer(100, action);
44460         }
44461         return this;
44462     },
44463
44464     /**
44465      * Shortcut to do a submit action.
44466      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44467      * @return {BasicForm} this
44468      */
44469     submit : function(options){
44470         this.doAction('submit', options);
44471         return this;
44472     },
44473
44474     /**
44475      * Shortcut to do a load action.
44476      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44477      * @return {BasicForm} this
44478      */
44479     load : function(options){
44480         this.doAction('load', options);
44481         return this;
44482     },
44483
44484     /**
44485      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44486      * @param {Record} record The record to edit
44487      * @return {BasicForm} this
44488      */
44489     updateRecord : function(record){
44490         record.beginEdit();
44491         var fs = record.fields;
44492         fs.each(function(f){
44493             var field = this.findField(f.name);
44494             if(field){
44495                 record.set(f.name, field.getValue());
44496             }
44497         }, this);
44498         record.endEdit();
44499         return this;
44500     },
44501
44502     /**
44503      * Loads an Roo.data.Record into this form.
44504      * @param {Record} record The record to load
44505      * @return {BasicForm} this
44506      */
44507     loadRecord : function(record){
44508         this.setValues(record.data);
44509         return this;
44510     },
44511
44512     // private
44513     beforeAction : function(action){
44514         var o = action.options;
44515         
44516        
44517         if(this.waitMsgTarget === true){
44518             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44519         }else if(this.waitMsgTarget){
44520             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44521             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44522         }else {
44523             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44524         }
44525          
44526     },
44527
44528     // private
44529     afterAction : function(action, success){
44530         this.activeAction = null;
44531         var o = action.options;
44532         
44533         if(this.waitMsgTarget === true){
44534             this.el.unmask();
44535         }else if(this.waitMsgTarget){
44536             this.waitMsgTarget.unmask();
44537         }else{
44538             Roo.MessageBox.updateProgress(1);
44539             Roo.MessageBox.hide();
44540         }
44541          
44542         if(success){
44543             if(o.reset){
44544                 this.reset();
44545             }
44546             Roo.callback(o.success, o.scope, [this, action]);
44547             this.fireEvent('actioncomplete', this, action);
44548             
44549         }else{
44550             
44551             // failure condition..
44552             // we have a scenario where updates need confirming.
44553             // eg. if a locking scenario exists..
44554             // we look for { errors : { needs_confirm : true }} in the response.
44555             if (
44556                 (typeof(action.result) != 'undefined')  &&
44557                 (typeof(action.result.errors) != 'undefined')  &&
44558                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44559            ){
44560                 var _t = this;
44561                 Roo.MessageBox.confirm(
44562                     "Change requires confirmation",
44563                     action.result.errorMsg,
44564                     function(r) {
44565                         if (r != 'yes') {
44566                             return;
44567                         }
44568                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44569                     }
44570                     
44571                 );
44572                 
44573                 
44574                 
44575                 return;
44576             }
44577             
44578             Roo.callback(o.failure, o.scope, [this, action]);
44579             // show an error message if no failed handler is set..
44580             if (!this.hasListener('actionfailed')) {
44581                 Roo.MessageBox.alert("Error",
44582                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44583                         action.result.errorMsg :
44584                         "Saving Failed, please check your entries or try again"
44585                 );
44586             }
44587             
44588             this.fireEvent('actionfailed', this, action);
44589         }
44590         
44591     },
44592
44593     /**
44594      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44595      * @param {String} id The value to search for
44596      * @return Field
44597      */
44598     findField : function(id){
44599         var field = this.items.get(id);
44600         if(!field){
44601             this.items.each(function(f){
44602                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44603                     field = f;
44604                     return false;
44605                 }
44606             });
44607         }
44608         return field || null;
44609     },
44610
44611     /**
44612      * Add a secondary form to this one, 
44613      * Used to provide tabbed forms. One form is primary, with hidden values 
44614      * which mirror the elements from the other forms.
44615      * 
44616      * @param {Roo.form.Form} form to add.
44617      * 
44618      */
44619     addForm : function(form)
44620     {
44621        
44622         if (this.childForms.indexOf(form) > -1) {
44623             // already added..
44624             return;
44625         }
44626         this.childForms.push(form);
44627         var n = '';
44628         Roo.each(form.allItems, function (fe) {
44629             
44630             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44631             if (this.findField(n)) { // already added..
44632                 return;
44633             }
44634             var add = new Roo.form.Hidden({
44635                 name : n
44636             });
44637             add.render(this.el);
44638             
44639             this.add( add );
44640         }, this);
44641         
44642     },
44643     /**
44644      * Mark fields in this form invalid in bulk.
44645      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44646      * @return {BasicForm} this
44647      */
44648     markInvalid : function(errors){
44649         if(errors instanceof Array){
44650             for(var i = 0, len = errors.length; i < len; i++){
44651                 var fieldError = errors[i];
44652                 var f = this.findField(fieldError.id);
44653                 if(f){
44654                     f.markInvalid(fieldError.msg);
44655                 }
44656             }
44657         }else{
44658             var field, id;
44659             for(id in errors){
44660                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44661                     field.markInvalid(errors[id]);
44662                 }
44663             }
44664         }
44665         Roo.each(this.childForms || [], function (f) {
44666             f.markInvalid(errors);
44667         });
44668         
44669         return this;
44670     },
44671
44672     /**
44673      * Set values for fields in this form in bulk.
44674      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44675      * @return {BasicForm} this
44676      */
44677     setValues : function(values){
44678         if(values instanceof Array){ // array of objects
44679             for(var i = 0, len = values.length; i < len; i++){
44680                 var v = values[i];
44681                 var f = this.findField(v.id);
44682                 if(f){
44683                     f.setValue(v.value);
44684                     if(this.trackResetOnLoad){
44685                         f.originalValue = f.getValue();
44686                     }
44687                 }
44688             }
44689         }else{ // object hash
44690             var field, id;
44691             for(id in values){
44692                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44693                     
44694                     if (field.setFromData && 
44695                         field.valueField && 
44696                         field.displayField &&
44697                         // combos' with local stores can 
44698                         // be queried via setValue()
44699                         // to set their value..
44700                         (field.store && !field.store.isLocal)
44701                         ) {
44702                         // it's a combo
44703                         var sd = { };
44704                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44705                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44706                         field.setFromData(sd);
44707                         
44708                     } else {
44709                         field.setValue(values[id]);
44710                     }
44711                     
44712                     
44713                     if(this.trackResetOnLoad){
44714                         field.originalValue = field.getValue();
44715                     }
44716                 }
44717             }
44718         }
44719          
44720         Roo.each(this.childForms || [], function (f) {
44721             f.setValues(values);
44722         });
44723                 
44724         return this;
44725     },
44726
44727     /**
44728      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44729      * they are returned as an array.
44730      * @param {Boolean} asString
44731      * @return {Object}
44732      */
44733     getValues : function(asString){
44734         if (this.childForms) {
44735             // copy values from the child forms
44736             Roo.each(this.childForms, function (f) {
44737                 this.setValues(f.getValues());
44738             }, this);
44739         }
44740         
44741         
44742         
44743         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44744         if(asString === true){
44745             return fs;
44746         }
44747         return Roo.urlDecode(fs);
44748     },
44749     
44750     /**
44751      * Returns the fields in this form as an object with key/value pairs. 
44752      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44753      * @return {Object}
44754      */
44755     getFieldValues : function(with_hidden)
44756     {
44757         if (this.childForms) {
44758             // copy values from the child forms
44759             // should this call getFieldValues - probably not as we do not currently copy
44760             // hidden fields when we generate..
44761             Roo.each(this.childForms, function (f) {
44762                 this.setValues(f.getValues());
44763             }, this);
44764         }
44765         
44766         var ret = {};
44767         this.items.each(function(f){
44768             if (!f.getName()) {
44769                 return;
44770             }
44771             var v = f.getValue();
44772             if (f.inputType =='radio') {
44773                 if (typeof(ret[f.getName()]) == 'undefined') {
44774                     ret[f.getName()] = ''; // empty..
44775                 }
44776                 
44777                 if (!f.el.dom.checked) {
44778                     return;
44779                     
44780                 }
44781                 v = f.el.dom.value;
44782                 
44783             }
44784             
44785             // not sure if this supported any more..
44786             if ((typeof(v) == 'object') && f.getRawValue) {
44787                 v = f.getRawValue() ; // dates..
44788             }
44789             // combo boxes where name != hiddenName...
44790             if (f.name != f.getName()) {
44791                 ret[f.name] = f.getRawValue();
44792             }
44793             ret[f.getName()] = v;
44794         });
44795         
44796         return ret;
44797     },
44798
44799     /**
44800      * Clears all invalid messages in this form.
44801      * @return {BasicForm} this
44802      */
44803     clearInvalid : function(){
44804         this.items.each(function(f){
44805            f.clearInvalid();
44806         });
44807         
44808         Roo.each(this.childForms || [], function (f) {
44809             f.clearInvalid();
44810         });
44811         
44812         
44813         return this;
44814     },
44815
44816     /**
44817      * Resets this form.
44818      * @return {BasicForm} this
44819      */
44820     reset : function(){
44821         this.items.each(function(f){
44822             f.reset();
44823         });
44824         
44825         Roo.each(this.childForms || [], function (f) {
44826             f.reset();
44827         });
44828        
44829         
44830         return this;
44831     },
44832
44833     /**
44834      * Add Roo.form components to this form.
44835      * @param {Field} field1
44836      * @param {Field} field2 (optional)
44837      * @param {Field} etc (optional)
44838      * @return {BasicForm} this
44839      */
44840     add : function(){
44841         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44842         return this;
44843     },
44844
44845
44846     /**
44847      * Removes a field from the items collection (does NOT remove its markup).
44848      * @param {Field} field
44849      * @return {BasicForm} this
44850      */
44851     remove : function(field){
44852         this.items.remove(field);
44853         return this;
44854     },
44855
44856     /**
44857      * Looks at the fields in this form, checks them for an id attribute,
44858      * and calls applyTo on the existing dom element with that id.
44859      * @return {BasicForm} this
44860      */
44861     render : function(){
44862         this.items.each(function(f){
44863             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44864                 f.applyTo(f.id);
44865             }
44866         });
44867         return this;
44868     },
44869
44870     /**
44871      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44872      * @param {Object} values
44873      * @return {BasicForm} this
44874      */
44875     applyToFields : function(o){
44876         this.items.each(function(f){
44877            Roo.apply(f, o);
44878         });
44879         return this;
44880     },
44881
44882     /**
44883      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44884      * @param {Object} values
44885      * @return {BasicForm} this
44886      */
44887     applyIfToFields : function(o){
44888         this.items.each(function(f){
44889            Roo.applyIf(f, o);
44890         });
44891         return this;
44892     }
44893 });
44894
44895 // back compat
44896 Roo.BasicForm = Roo.form.BasicForm;/*
44897  * Based on:
44898  * Ext JS Library 1.1.1
44899  * Copyright(c) 2006-2007, Ext JS, LLC.
44900  *
44901  * Originally Released Under LGPL - original licence link has changed is not relivant.
44902  *
44903  * Fork - LGPL
44904  * <script type="text/javascript">
44905  */
44906
44907 /**
44908  * @class Roo.form.Form
44909  * @extends Roo.form.BasicForm
44910  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44911  * @constructor
44912  * @param {Object} config Configuration options
44913  */
44914 Roo.form.Form = function(config){
44915     var xitems =  [];
44916     if (config.items) {
44917         xitems = config.items;
44918         delete config.items;
44919     }
44920    
44921     
44922     Roo.form.Form.superclass.constructor.call(this, null, config);
44923     this.url = this.url || this.action;
44924     if(!this.root){
44925         this.root = new Roo.form.Layout(Roo.applyIf({
44926             id: Roo.id()
44927         }, config));
44928     }
44929     this.active = this.root;
44930     /**
44931      * Array of all the buttons that have been added to this form via {@link addButton}
44932      * @type Array
44933      */
44934     this.buttons = [];
44935     this.allItems = [];
44936     this.addEvents({
44937         /**
44938          * @event clientvalidation
44939          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44940          * @param {Form} this
44941          * @param {Boolean} valid true if the form has passed client-side validation
44942          */
44943         clientvalidation: true,
44944         /**
44945          * @event rendered
44946          * Fires when the form is rendered
44947          * @param {Roo.form.Form} form
44948          */
44949         rendered : true
44950     });
44951     
44952     if (this.progressUrl) {
44953             // push a hidden field onto the list of fields..
44954             this.addxtype( {
44955                     xns: Roo.form, 
44956                     xtype : 'Hidden', 
44957                     name : 'UPLOAD_IDENTIFIER' 
44958             });
44959         }
44960         
44961     
44962     Roo.each(xitems, this.addxtype, this);
44963     
44964     
44965     
44966 };
44967
44968 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44969     /**
44970      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44971      */
44972     /**
44973      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44974      */
44975     /**
44976      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44977      */
44978     buttonAlign:'center',
44979
44980     /**
44981      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44982      */
44983     minButtonWidth:75,
44984
44985     /**
44986      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44987      * This property cascades to child containers if not set.
44988      */
44989     labelAlign:'left',
44990
44991     /**
44992      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44993      * fires a looping event with that state. This is required to bind buttons to the valid
44994      * state using the config value formBind:true on the button.
44995      */
44996     monitorValid : false,
44997
44998     /**
44999      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45000      */
45001     monitorPoll : 200,
45002     
45003     /**
45004      * @cfg {String} progressUrl - Url to return progress data 
45005      */
45006     
45007     progressUrl : false,
45008   
45009     /**
45010      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45011      * fields are added and the column is closed. If no fields are passed the column remains open
45012      * until end() is called.
45013      * @param {Object} config The config to pass to the column
45014      * @param {Field} field1 (optional)
45015      * @param {Field} field2 (optional)
45016      * @param {Field} etc (optional)
45017      * @return Column The column container object
45018      */
45019     column : function(c){
45020         var col = new Roo.form.Column(c);
45021         this.start(col);
45022         if(arguments.length > 1){ // duplicate code required because of Opera
45023             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45024             this.end();
45025         }
45026         return col;
45027     },
45028
45029     /**
45030      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45031      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45032      * until end() is called.
45033      * @param {Object} config The config to pass to the fieldset
45034      * @param {Field} field1 (optional)
45035      * @param {Field} field2 (optional)
45036      * @param {Field} etc (optional)
45037      * @return FieldSet The fieldset container object
45038      */
45039     fieldset : function(c){
45040         var fs = new Roo.form.FieldSet(c);
45041         this.start(fs);
45042         if(arguments.length > 1){ // duplicate code required because of Opera
45043             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45044             this.end();
45045         }
45046         return fs;
45047     },
45048
45049     /**
45050      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45051      * fields are added and the container is closed. If no fields are passed the container remains open
45052      * until end() is called.
45053      * @param {Object} config The config to pass to the Layout
45054      * @param {Field} field1 (optional)
45055      * @param {Field} field2 (optional)
45056      * @param {Field} etc (optional)
45057      * @return Layout The container object
45058      */
45059     container : function(c){
45060         var l = new Roo.form.Layout(c);
45061         this.start(l);
45062         if(arguments.length > 1){ // duplicate code required because of Opera
45063             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45064             this.end();
45065         }
45066         return l;
45067     },
45068
45069     /**
45070      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45071      * @param {Object} container A Roo.form.Layout or subclass of Layout
45072      * @return {Form} this
45073      */
45074     start : function(c){
45075         // cascade label info
45076         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45077         this.active.stack.push(c);
45078         c.ownerCt = this.active;
45079         this.active = c;
45080         return this;
45081     },
45082
45083     /**
45084      * Closes the current open container
45085      * @return {Form} this
45086      */
45087     end : function(){
45088         if(this.active == this.root){
45089             return this;
45090         }
45091         this.active = this.active.ownerCt;
45092         return this;
45093     },
45094
45095     /**
45096      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45097      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45098      * as the label of the field.
45099      * @param {Field} field1
45100      * @param {Field} field2 (optional)
45101      * @param {Field} etc. (optional)
45102      * @return {Form} this
45103      */
45104     add : function(){
45105         this.active.stack.push.apply(this.active.stack, arguments);
45106         this.allItems.push.apply(this.allItems,arguments);
45107         var r = [];
45108         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45109             if(a[i].isFormField){
45110                 r.push(a[i]);
45111             }
45112         }
45113         if(r.length > 0){
45114             Roo.form.Form.superclass.add.apply(this, r);
45115         }
45116         return this;
45117     },
45118     
45119
45120     
45121     
45122     
45123      /**
45124      * Find any element that has been added to a form, using it's ID or name
45125      * This can include framesets, columns etc. along with regular fields..
45126      * @param {String} id - id or name to find.
45127      
45128      * @return {Element} e - or false if nothing found.
45129      */
45130     findbyId : function(id)
45131     {
45132         var ret = false;
45133         if (!id) {
45134             return ret;
45135         }
45136         Roo.each(this.allItems, function(f){
45137             if (f.id == id || f.name == id ){
45138                 ret = f;
45139                 return false;
45140             }
45141         });
45142         return ret;
45143     },
45144
45145     
45146     
45147     /**
45148      * Render this form into the passed container. This should only be called once!
45149      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45150      * @return {Form} this
45151      */
45152     render : function(ct)
45153     {
45154         
45155         
45156         
45157         ct = Roo.get(ct);
45158         var o = this.autoCreate || {
45159             tag: 'form',
45160             method : this.method || 'POST',
45161             id : this.id || Roo.id()
45162         };
45163         this.initEl(ct.createChild(o));
45164
45165         this.root.render(this.el);
45166         
45167        
45168              
45169         this.items.each(function(f){
45170             f.render('x-form-el-'+f.id);
45171         });
45172
45173         if(this.buttons.length > 0){
45174             // tables are required to maintain order and for correct IE layout
45175             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45176                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45177                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45178             }}, null, true);
45179             var tr = tb.getElementsByTagName('tr')[0];
45180             for(var i = 0, len = this.buttons.length; i < len; i++) {
45181                 var b = this.buttons[i];
45182                 var td = document.createElement('td');
45183                 td.className = 'x-form-btn-td';
45184                 b.render(tr.appendChild(td));
45185             }
45186         }
45187         if(this.monitorValid){ // initialize after render
45188             this.startMonitoring();
45189         }
45190         this.fireEvent('rendered', this);
45191         return this;
45192     },
45193
45194     /**
45195      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45196      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45197      * object or a valid Roo.DomHelper element config
45198      * @param {Function} handler The function called when the button is clicked
45199      * @param {Object} scope (optional) The scope of the handler function
45200      * @return {Roo.Button}
45201      */
45202     addButton : function(config, handler, scope){
45203         var bc = {
45204             handler: handler,
45205             scope: scope,
45206             minWidth: this.minButtonWidth,
45207             hideParent:true
45208         };
45209         if(typeof config == "string"){
45210             bc.text = config;
45211         }else{
45212             Roo.apply(bc, config);
45213         }
45214         var btn = new Roo.Button(null, bc);
45215         this.buttons.push(btn);
45216         return btn;
45217     },
45218
45219      /**
45220      * Adds a series of form elements (using the xtype property as the factory method.
45221      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45222      * @param {Object} config 
45223      */
45224     
45225     addxtype : function()
45226     {
45227         var ar = Array.prototype.slice.call(arguments, 0);
45228         var ret = false;
45229         for(var i = 0; i < ar.length; i++) {
45230             if (!ar[i]) {
45231                 continue; // skip -- if this happends something invalid got sent, we 
45232                 // should ignore it, as basically that interface element will not show up
45233                 // and that should be pretty obvious!!
45234             }
45235             
45236             if (Roo.form[ar[i].xtype]) {
45237                 ar[i].form = this;
45238                 var fe = Roo.factory(ar[i], Roo.form);
45239                 if (!ret) {
45240                     ret = fe;
45241                 }
45242                 fe.form = this;
45243                 if (fe.store) {
45244                     fe.store.form = this;
45245                 }
45246                 if (fe.isLayout) {  
45247                          
45248                     this.start(fe);
45249                     this.allItems.push(fe);
45250                     if (fe.items && fe.addxtype) {
45251                         fe.addxtype.apply(fe, fe.items);
45252                         delete fe.items;
45253                     }
45254                      this.end();
45255                     continue;
45256                 }
45257                 
45258                 
45259                  
45260                 this.add(fe);
45261               //  console.log('adding ' + ar[i].xtype);
45262             }
45263             if (ar[i].xtype == 'Button') {  
45264                 //console.log('adding button');
45265                 //console.log(ar[i]);
45266                 this.addButton(ar[i]);
45267                 this.allItems.push(fe);
45268                 continue;
45269             }
45270             
45271             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45272                 alert('end is not supported on xtype any more, use items');
45273             //    this.end();
45274             //    //console.log('adding end');
45275             }
45276             
45277         }
45278         return ret;
45279     },
45280     
45281     /**
45282      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45283      * option "monitorValid"
45284      */
45285     startMonitoring : function(){
45286         if(!this.bound){
45287             this.bound = true;
45288             Roo.TaskMgr.start({
45289                 run : this.bindHandler,
45290                 interval : this.monitorPoll || 200,
45291                 scope: this
45292             });
45293         }
45294     },
45295
45296     /**
45297      * Stops monitoring of the valid state of this form
45298      */
45299     stopMonitoring : function(){
45300         this.bound = false;
45301     },
45302
45303     // private
45304     bindHandler : function(){
45305         if(!this.bound){
45306             return false; // stops binding
45307         }
45308         var valid = true;
45309         this.items.each(function(f){
45310             if(!f.isValid(true)){
45311                 valid = false;
45312                 return false;
45313             }
45314         });
45315         for(var i = 0, len = this.buttons.length; i < len; i++){
45316             var btn = this.buttons[i];
45317             if(btn.formBind === true && btn.disabled === valid){
45318                 btn.setDisabled(!valid);
45319             }
45320         }
45321         this.fireEvent('clientvalidation', this, valid);
45322     }
45323     
45324     
45325     
45326     
45327     
45328     
45329     
45330     
45331 });
45332
45333
45334 // back compat
45335 Roo.Form = Roo.form.Form;
45336 /*
45337  * Based on:
45338  * Ext JS Library 1.1.1
45339  * Copyright(c) 2006-2007, Ext JS, LLC.
45340  *
45341  * Originally Released Under LGPL - original licence link has changed is not relivant.
45342  *
45343  * Fork - LGPL
45344  * <script type="text/javascript">
45345  */
45346
45347 // as we use this in bootstrap.
45348 Roo.namespace('Roo.form');
45349  /**
45350  * @class Roo.form.Action
45351  * Internal Class used to handle form actions
45352  * @constructor
45353  * @param {Roo.form.BasicForm} el The form element or its id
45354  * @param {Object} config Configuration options
45355  */
45356
45357  
45358  
45359 // define the action interface
45360 Roo.form.Action = function(form, options){
45361     this.form = form;
45362     this.options = options || {};
45363 };
45364 /**
45365  * Client Validation Failed
45366  * @const 
45367  */
45368 Roo.form.Action.CLIENT_INVALID = 'client';
45369 /**
45370  * Server Validation Failed
45371  * @const 
45372  */
45373 Roo.form.Action.SERVER_INVALID = 'server';
45374  /**
45375  * Connect to Server Failed
45376  * @const 
45377  */
45378 Roo.form.Action.CONNECT_FAILURE = 'connect';
45379 /**
45380  * Reading Data from Server Failed
45381  * @const 
45382  */
45383 Roo.form.Action.LOAD_FAILURE = 'load';
45384
45385 Roo.form.Action.prototype = {
45386     type : 'default',
45387     failureType : undefined,
45388     response : undefined,
45389     result : undefined,
45390
45391     // interface method
45392     run : function(options){
45393
45394     },
45395
45396     // interface method
45397     success : function(response){
45398
45399     },
45400
45401     // interface method
45402     handleResponse : function(response){
45403
45404     },
45405
45406     // default connection failure
45407     failure : function(response){
45408         
45409         this.response = response;
45410         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45411         this.form.afterAction(this, false);
45412     },
45413
45414     processResponse : function(response){
45415         this.response = response;
45416         if(!response.responseText){
45417             return true;
45418         }
45419         this.result = this.handleResponse(response);
45420         return this.result;
45421     },
45422
45423     // utility functions used internally
45424     getUrl : function(appendParams){
45425         var url = this.options.url || this.form.url || this.form.el.dom.action;
45426         if(appendParams){
45427             var p = this.getParams();
45428             if(p){
45429                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45430             }
45431         }
45432         return url;
45433     },
45434
45435     getMethod : function(){
45436         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45437     },
45438
45439     getParams : function(){
45440         var bp = this.form.baseParams;
45441         var p = this.options.params;
45442         if(p){
45443             if(typeof p == "object"){
45444                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45445             }else if(typeof p == 'string' && bp){
45446                 p += '&' + Roo.urlEncode(bp);
45447             }
45448         }else if(bp){
45449             p = Roo.urlEncode(bp);
45450         }
45451         return p;
45452     },
45453
45454     createCallback : function(){
45455         return {
45456             success: this.success,
45457             failure: this.failure,
45458             scope: this,
45459             timeout: (this.form.timeout*1000),
45460             upload: this.form.fileUpload ? this.success : undefined
45461         };
45462     }
45463 };
45464
45465 Roo.form.Action.Submit = function(form, options){
45466     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45467 };
45468
45469 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45470     type : 'submit',
45471
45472     haveProgress : false,
45473     uploadComplete : false,
45474     
45475     // uploadProgress indicator.
45476     uploadProgress : function()
45477     {
45478         if (!this.form.progressUrl) {
45479             return;
45480         }
45481         
45482         if (!this.haveProgress) {
45483             Roo.MessageBox.progress("Uploading", "Uploading");
45484         }
45485         if (this.uploadComplete) {
45486            Roo.MessageBox.hide();
45487            return;
45488         }
45489         
45490         this.haveProgress = true;
45491    
45492         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45493         
45494         var c = new Roo.data.Connection();
45495         c.request({
45496             url : this.form.progressUrl,
45497             params: {
45498                 id : uid
45499             },
45500             method: 'GET',
45501             success : function(req){
45502                //console.log(data);
45503                 var rdata = false;
45504                 var edata;
45505                 try  {
45506                    rdata = Roo.decode(req.responseText)
45507                 } catch (e) {
45508                     Roo.log("Invalid data from server..");
45509                     Roo.log(edata);
45510                     return;
45511                 }
45512                 if (!rdata || !rdata.success) {
45513                     Roo.log(rdata);
45514                     Roo.MessageBox.alert(Roo.encode(rdata));
45515                     return;
45516                 }
45517                 var data = rdata.data;
45518                 
45519                 if (this.uploadComplete) {
45520                    Roo.MessageBox.hide();
45521                    return;
45522                 }
45523                    
45524                 if (data){
45525                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45526                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45527                     );
45528                 }
45529                 this.uploadProgress.defer(2000,this);
45530             },
45531        
45532             failure: function(data) {
45533                 Roo.log('progress url failed ');
45534                 Roo.log(data);
45535             },
45536             scope : this
45537         });
45538            
45539     },
45540     
45541     
45542     run : function()
45543     {
45544         // run get Values on the form, so it syncs any secondary forms.
45545         this.form.getValues();
45546         
45547         var o = this.options;
45548         var method = this.getMethod();
45549         var isPost = method == 'POST';
45550         if(o.clientValidation === false || this.form.isValid()){
45551             
45552             if (this.form.progressUrl) {
45553                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45554                     (new Date() * 1) + '' + Math.random());
45555                     
45556             } 
45557             
45558             
45559             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45560                 form:this.form.el.dom,
45561                 url:this.getUrl(!isPost),
45562                 method: method,
45563                 params:isPost ? this.getParams() : null,
45564                 isUpload: this.form.fileUpload
45565             }));
45566             
45567             this.uploadProgress();
45568
45569         }else if (o.clientValidation !== false){ // client validation failed
45570             this.failureType = Roo.form.Action.CLIENT_INVALID;
45571             this.form.afterAction(this, false);
45572         }
45573     },
45574
45575     success : function(response)
45576     {
45577         this.uploadComplete= true;
45578         if (this.haveProgress) {
45579             Roo.MessageBox.hide();
45580         }
45581         
45582         
45583         var result = this.processResponse(response);
45584         if(result === true || result.success){
45585             this.form.afterAction(this, true);
45586             return;
45587         }
45588         if(result.errors){
45589             this.form.markInvalid(result.errors);
45590             this.failureType = Roo.form.Action.SERVER_INVALID;
45591         }
45592         this.form.afterAction(this, false);
45593     },
45594     failure : function(response)
45595     {
45596         this.uploadComplete= true;
45597         if (this.haveProgress) {
45598             Roo.MessageBox.hide();
45599         }
45600         
45601         this.response = response;
45602         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45603         this.form.afterAction(this, false);
45604     },
45605     
45606     handleResponse : function(response){
45607         if(this.form.errorReader){
45608             var rs = this.form.errorReader.read(response);
45609             var errors = [];
45610             if(rs.records){
45611                 for(var i = 0, len = rs.records.length; i < len; i++) {
45612                     var r = rs.records[i];
45613                     errors[i] = r.data;
45614                 }
45615             }
45616             if(errors.length < 1){
45617                 errors = null;
45618             }
45619             return {
45620                 success : rs.success,
45621                 errors : errors
45622             };
45623         }
45624         var ret = false;
45625         try {
45626             ret = Roo.decode(response.responseText);
45627         } catch (e) {
45628             ret = {
45629                 success: false,
45630                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45631                 errors : []
45632             };
45633         }
45634         return ret;
45635         
45636     }
45637 });
45638
45639
45640 Roo.form.Action.Load = function(form, options){
45641     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45642     this.reader = this.form.reader;
45643 };
45644
45645 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45646     type : 'load',
45647
45648     run : function(){
45649         
45650         Roo.Ajax.request(Roo.apply(
45651                 this.createCallback(), {
45652                     method:this.getMethod(),
45653                     url:this.getUrl(false),
45654                     params:this.getParams()
45655         }));
45656     },
45657
45658     success : function(response){
45659         
45660         var result = this.processResponse(response);
45661         if(result === true || !result.success || !result.data){
45662             this.failureType = Roo.form.Action.LOAD_FAILURE;
45663             this.form.afterAction(this, false);
45664             return;
45665         }
45666         this.form.clearInvalid();
45667         this.form.setValues(result.data);
45668         this.form.afterAction(this, true);
45669     },
45670
45671     handleResponse : function(response){
45672         if(this.form.reader){
45673             var rs = this.form.reader.read(response);
45674             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45675             return {
45676                 success : rs.success,
45677                 data : data
45678             };
45679         }
45680         return Roo.decode(response.responseText);
45681     }
45682 });
45683
45684 Roo.form.Action.ACTION_TYPES = {
45685     'load' : Roo.form.Action.Load,
45686     'submit' : Roo.form.Action.Submit
45687 };/*
45688  * Based on:
45689  * Ext JS Library 1.1.1
45690  * Copyright(c) 2006-2007, Ext JS, LLC.
45691  *
45692  * Originally Released Under LGPL - original licence link has changed is not relivant.
45693  *
45694  * Fork - LGPL
45695  * <script type="text/javascript">
45696  */
45697  
45698 /**
45699  * @class Roo.form.Layout
45700  * @extends Roo.Component
45701  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45702  * @constructor
45703  * @param {Object} config Configuration options
45704  */
45705 Roo.form.Layout = function(config){
45706     var xitems = [];
45707     if (config.items) {
45708         xitems = config.items;
45709         delete config.items;
45710     }
45711     Roo.form.Layout.superclass.constructor.call(this, config);
45712     this.stack = [];
45713     Roo.each(xitems, this.addxtype, this);
45714      
45715 };
45716
45717 Roo.extend(Roo.form.Layout, Roo.Component, {
45718     /**
45719      * @cfg {String/Object} autoCreate
45720      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45721      */
45722     /**
45723      * @cfg {String/Object/Function} style
45724      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45725      * a function which returns such a specification.
45726      */
45727     /**
45728      * @cfg {String} labelAlign
45729      * Valid values are "left," "top" and "right" (defaults to "left")
45730      */
45731     /**
45732      * @cfg {Number} labelWidth
45733      * Fixed width in pixels of all field labels (defaults to undefined)
45734      */
45735     /**
45736      * @cfg {Boolean} clear
45737      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45738      */
45739     clear : true,
45740     /**
45741      * @cfg {String} labelSeparator
45742      * The separator to use after field labels (defaults to ':')
45743      */
45744     labelSeparator : ':',
45745     /**
45746      * @cfg {Boolean} hideLabels
45747      * True to suppress the display of field labels in this layout (defaults to false)
45748      */
45749     hideLabels : false,
45750
45751     // private
45752     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45753     
45754     isLayout : true,
45755     
45756     // private
45757     onRender : function(ct, position){
45758         if(this.el){ // from markup
45759             this.el = Roo.get(this.el);
45760         }else {  // generate
45761             var cfg = this.getAutoCreate();
45762             this.el = ct.createChild(cfg, position);
45763         }
45764         if(this.style){
45765             this.el.applyStyles(this.style);
45766         }
45767         if(this.labelAlign){
45768             this.el.addClass('x-form-label-'+this.labelAlign);
45769         }
45770         if(this.hideLabels){
45771             this.labelStyle = "display:none";
45772             this.elementStyle = "padding-left:0;";
45773         }else{
45774             if(typeof this.labelWidth == 'number'){
45775                 this.labelStyle = "width:"+this.labelWidth+"px;";
45776                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45777             }
45778             if(this.labelAlign == 'top'){
45779                 this.labelStyle = "width:auto;";
45780                 this.elementStyle = "padding-left:0;";
45781             }
45782         }
45783         var stack = this.stack;
45784         var slen = stack.length;
45785         if(slen > 0){
45786             if(!this.fieldTpl){
45787                 var t = new Roo.Template(
45788                     '<div class="x-form-item {5}">',
45789                         '<label for="{0}" style="{2}">{1}{4}</label>',
45790                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45791                         '</div>',
45792                     '</div><div class="x-form-clear-left"></div>'
45793                 );
45794                 t.disableFormats = true;
45795                 t.compile();
45796                 Roo.form.Layout.prototype.fieldTpl = t;
45797             }
45798             for(var i = 0; i < slen; i++) {
45799                 if(stack[i].isFormField){
45800                     this.renderField(stack[i]);
45801                 }else{
45802                     this.renderComponent(stack[i]);
45803                 }
45804             }
45805         }
45806         if(this.clear){
45807             this.el.createChild({cls:'x-form-clear'});
45808         }
45809     },
45810
45811     // private
45812     renderField : function(f){
45813         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45814                f.id, //0
45815                f.fieldLabel, //1
45816                f.labelStyle||this.labelStyle||'', //2
45817                this.elementStyle||'', //3
45818                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45819                f.itemCls||this.itemCls||''  //5
45820        ], true).getPrevSibling());
45821     },
45822
45823     // private
45824     renderComponent : function(c){
45825         c.render(c.isLayout ? this.el : this.el.createChild());    
45826     },
45827     /**
45828      * Adds a object form elements (using the xtype property as the factory method.)
45829      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45830      * @param {Object} config 
45831      */
45832     addxtype : function(o)
45833     {
45834         // create the lement.
45835         o.form = this.form;
45836         var fe = Roo.factory(o, Roo.form);
45837         this.form.allItems.push(fe);
45838         this.stack.push(fe);
45839         
45840         if (fe.isFormField) {
45841             this.form.items.add(fe);
45842         }
45843          
45844         return fe;
45845     }
45846 });
45847
45848 /**
45849  * @class Roo.form.Column
45850  * @extends Roo.form.Layout
45851  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45852  * @constructor
45853  * @param {Object} config Configuration options
45854  */
45855 Roo.form.Column = function(config){
45856     Roo.form.Column.superclass.constructor.call(this, config);
45857 };
45858
45859 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45860     /**
45861      * @cfg {Number/String} width
45862      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45863      */
45864     /**
45865      * @cfg {String/Object} autoCreate
45866      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45867      */
45868
45869     // private
45870     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45871
45872     // private
45873     onRender : function(ct, position){
45874         Roo.form.Column.superclass.onRender.call(this, ct, position);
45875         if(this.width){
45876             this.el.setWidth(this.width);
45877         }
45878     }
45879 });
45880
45881
45882 /**
45883  * @class Roo.form.Row
45884  * @extends Roo.form.Layout
45885  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45886  * @constructor
45887  * @param {Object} config Configuration options
45888  */
45889
45890  
45891 Roo.form.Row = function(config){
45892     Roo.form.Row.superclass.constructor.call(this, config);
45893 };
45894  
45895 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45896       /**
45897      * @cfg {Number/String} width
45898      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45899      */
45900     /**
45901      * @cfg {Number/String} height
45902      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45903      */
45904     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45905     
45906     padWidth : 20,
45907     // private
45908     onRender : function(ct, position){
45909         //console.log('row render');
45910         if(!this.rowTpl){
45911             var t = new Roo.Template(
45912                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45913                     '<label for="{0}" style="{2}">{1}{4}</label>',
45914                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45915                     '</div>',
45916                 '</div>'
45917             );
45918             t.disableFormats = true;
45919             t.compile();
45920             Roo.form.Layout.prototype.rowTpl = t;
45921         }
45922         this.fieldTpl = this.rowTpl;
45923         
45924         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45925         var labelWidth = 100;
45926         
45927         if ((this.labelAlign != 'top')) {
45928             if (typeof this.labelWidth == 'number') {
45929                 labelWidth = this.labelWidth
45930             }
45931             this.padWidth =  20 + labelWidth;
45932             
45933         }
45934         
45935         Roo.form.Column.superclass.onRender.call(this, ct, position);
45936         if(this.width){
45937             this.el.setWidth(this.width);
45938         }
45939         if(this.height){
45940             this.el.setHeight(this.height);
45941         }
45942     },
45943     
45944     // private
45945     renderField : function(f){
45946         f.fieldEl = this.fieldTpl.append(this.el, [
45947                f.id, f.fieldLabel,
45948                f.labelStyle||this.labelStyle||'',
45949                this.elementStyle||'',
45950                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45951                f.itemCls||this.itemCls||'',
45952                f.width ? f.width + this.padWidth : 160 + this.padWidth
45953        ],true);
45954     }
45955 });
45956  
45957
45958 /**
45959  * @class Roo.form.FieldSet
45960  * @extends Roo.form.Layout
45961  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45962  * @constructor
45963  * @param {Object} config Configuration options
45964  */
45965 Roo.form.FieldSet = function(config){
45966     Roo.form.FieldSet.superclass.constructor.call(this, config);
45967 };
45968
45969 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45970     /**
45971      * @cfg {String} legend
45972      * The text to display as the legend for the FieldSet (defaults to '')
45973      */
45974     /**
45975      * @cfg {String/Object} autoCreate
45976      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45977      */
45978
45979     // private
45980     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45981
45982     // private
45983     onRender : function(ct, position){
45984         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45985         if(this.legend){
45986             this.setLegend(this.legend);
45987         }
45988     },
45989
45990     // private
45991     setLegend : function(text){
45992         if(this.rendered){
45993             this.el.child('legend').update(text);
45994         }
45995     }
45996 });/*
45997  * Based on:
45998  * Ext JS Library 1.1.1
45999  * Copyright(c) 2006-2007, Ext JS, LLC.
46000  *
46001  * Originally Released Under LGPL - original licence link has changed is not relivant.
46002  *
46003  * Fork - LGPL
46004  * <script type="text/javascript">
46005  */
46006 /**
46007  * @class Roo.form.VTypes
46008  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46009  * @singleton
46010  */
46011 Roo.form.VTypes = function(){
46012     // closure these in so they are only created once.
46013     var alpha = /^[a-zA-Z_]+$/;
46014     var alphanum = /^[a-zA-Z0-9_]+$/;
46015     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46016     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46017
46018     // All these messages and functions are configurable
46019     return {
46020         /**
46021          * The function used to validate email addresses
46022          * @param {String} value The email address
46023          */
46024         'email' : function(v){
46025             return email.test(v);
46026         },
46027         /**
46028          * The error text to display when the email validation function returns false
46029          * @type String
46030          */
46031         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46032         /**
46033          * The keystroke filter mask to be applied on email input
46034          * @type RegExp
46035          */
46036         'emailMask' : /[a-z0-9_\.\-@]/i,
46037
46038         /**
46039          * The function used to validate URLs
46040          * @param {String} value The URL
46041          */
46042         'url' : function(v){
46043             return url.test(v);
46044         },
46045         /**
46046          * The error text to display when the url validation function returns false
46047          * @type String
46048          */
46049         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46050         
46051         /**
46052          * The function used to validate alpha values
46053          * @param {String} value The value
46054          */
46055         'alpha' : function(v){
46056             return alpha.test(v);
46057         },
46058         /**
46059          * The error text to display when the alpha validation function returns false
46060          * @type String
46061          */
46062         'alphaText' : 'This field should only contain letters and _',
46063         /**
46064          * The keystroke filter mask to be applied on alpha input
46065          * @type RegExp
46066          */
46067         'alphaMask' : /[a-z_]/i,
46068
46069         /**
46070          * The function used to validate alphanumeric values
46071          * @param {String} value The value
46072          */
46073         'alphanum' : function(v){
46074             return alphanum.test(v);
46075         },
46076         /**
46077          * The error text to display when the alphanumeric validation function returns false
46078          * @type String
46079          */
46080         'alphanumText' : 'This field should only contain letters, numbers and _',
46081         /**
46082          * The keystroke filter mask to be applied on alphanumeric input
46083          * @type RegExp
46084          */
46085         'alphanumMask' : /[a-z0-9_]/i
46086     };
46087 }();//<script type="text/javascript">
46088
46089 /**
46090  * @class Roo.form.FCKeditor
46091  * @extends Roo.form.TextArea
46092  * Wrapper around the FCKEditor http://www.fckeditor.net
46093  * @constructor
46094  * Creates a new FCKeditor
46095  * @param {Object} config Configuration options
46096  */
46097 Roo.form.FCKeditor = function(config){
46098     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46099     this.addEvents({
46100          /**
46101          * @event editorinit
46102          * Fired when the editor is initialized - you can add extra handlers here..
46103          * @param {FCKeditor} this
46104          * @param {Object} the FCK object.
46105          */
46106         editorinit : true
46107     });
46108     
46109     
46110 };
46111 Roo.form.FCKeditor.editors = { };
46112 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46113 {
46114     //defaultAutoCreate : {
46115     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46116     //},
46117     // private
46118     /**
46119      * @cfg {Object} fck options - see fck manual for details.
46120      */
46121     fckconfig : false,
46122     
46123     /**
46124      * @cfg {Object} fck toolbar set (Basic or Default)
46125      */
46126     toolbarSet : 'Basic',
46127     /**
46128      * @cfg {Object} fck BasePath
46129      */ 
46130     basePath : '/fckeditor/',
46131     
46132     
46133     frame : false,
46134     
46135     value : '',
46136     
46137    
46138     onRender : function(ct, position)
46139     {
46140         if(!this.el){
46141             this.defaultAutoCreate = {
46142                 tag: "textarea",
46143                 style:"width:300px;height:60px;",
46144                 autocomplete: "off"
46145             };
46146         }
46147         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46148         /*
46149         if(this.grow){
46150             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46151             if(this.preventScrollbars){
46152                 this.el.setStyle("overflow", "hidden");
46153             }
46154             this.el.setHeight(this.growMin);
46155         }
46156         */
46157         //console.log('onrender' + this.getId() );
46158         Roo.form.FCKeditor.editors[this.getId()] = this;
46159          
46160
46161         this.replaceTextarea() ;
46162         
46163     },
46164     
46165     getEditor : function() {
46166         return this.fckEditor;
46167     },
46168     /**
46169      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46170      * @param {Mixed} value The value to set
46171      */
46172     
46173     
46174     setValue : function(value)
46175     {
46176         //console.log('setValue: ' + value);
46177         
46178         if(typeof(value) == 'undefined') { // not sure why this is happending...
46179             return;
46180         }
46181         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46182         
46183         //if(!this.el || !this.getEditor()) {
46184         //    this.value = value;
46185             //this.setValue.defer(100,this,[value]);    
46186         //    return;
46187         //} 
46188         
46189         if(!this.getEditor()) {
46190             return;
46191         }
46192         
46193         this.getEditor().SetData(value);
46194         
46195         //
46196
46197     },
46198
46199     /**
46200      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46201      * @return {Mixed} value The field value
46202      */
46203     getValue : function()
46204     {
46205         
46206         if (this.frame && this.frame.dom.style.display == 'none') {
46207             return Roo.form.FCKeditor.superclass.getValue.call(this);
46208         }
46209         
46210         if(!this.el || !this.getEditor()) {
46211            
46212            // this.getValue.defer(100,this); 
46213             return this.value;
46214         }
46215        
46216         
46217         var value=this.getEditor().GetData();
46218         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46219         return Roo.form.FCKeditor.superclass.getValue.call(this);
46220         
46221
46222     },
46223
46224     /**
46225      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46226      * @return {Mixed} value The field value
46227      */
46228     getRawValue : function()
46229     {
46230         if (this.frame && this.frame.dom.style.display == 'none') {
46231             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46232         }
46233         
46234         if(!this.el || !this.getEditor()) {
46235             //this.getRawValue.defer(100,this); 
46236             return this.value;
46237             return;
46238         }
46239         
46240         
46241         
46242         var value=this.getEditor().GetData();
46243         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46244         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46245          
46246     },
46247     
46248     setSize : function(w,h) {
46249         
46250         
46251         
46252         //if (this.frame && this.frame.dom.style.display == 'none') {
46253         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46254         //    return;
46255         //}
46256         //if(!this.el || !this.getEditor()) {
46257         //    this.setSize.defer(100,this, [w,h]); 
46258         //    return;
46259         //}
46260         
46261         
46262         
46263         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46264         
46265         this.frame.dom.setAttribute('width', w);
46266         this.frame.dom.setAttribute('height', h);
46267         this.frame.setSize(w,h);
46268         
46269     },
46270     
46271     toggleSourceEdit : function(value) {
46272         
46273       
46274          
46275         this.el.dom.style.display = value ? '' : 'none';
46276         this.frame.dom.style.display = value ?  'none' : '';
46277         
46278     },
46279     
46280     
46281     focus: function(tag)
46282     {
46283         if (this.frame.dom.style.display == 'none') {
46284             return Roo.form.FCKeditor.superclass.focus.call(this);
46285         }
46286         if(!this.el || !this.getEditor()) {
46287             this.focus.defer(100,this, [tag]); 
46288             return;
46289         }
46290         
46291         
46292         
46293         
46294         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46295         this.getEditor().Focus();
46296         if (tgs.length) {
46297             if (!this.getEditor().Selection.GetSelection()) {
46298                 this.focus.defer(100,this, [tag]); 
46299                 return;
46300             }
46301             
46302             
46303             var r = this.getEditor().EditorDocument.createRange();
46304             r.setStart(tgs[0],0);
46305             r.setEnd(tgs[0],0);
46306             this.getEditor().Selection.GetSelection().removeAllRanges();
46307             this.getEditor().Selection.GetSelection().addRange(r);
46308             this.getEditor().Focus();
46309         }
46310         
46311     },
46312     
46313     
46314     
46315     replaceTextarea : function()
46316     {
46317         if ( document.getElementById( this.getId() + '___Frame' ) )
46318             return ;
46319         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46320         //{
46321             // We must check the elements firstly using the Id and then the name.
46322         var oTextarea = document.getElementById( this.getId() );
46323         
46324         var colElementsByName = document.getElementsByName( this.getId() ) ;
46325          
46326         oTextarea.style.display = 'none' ;
46327
46328         if ( oTextarea.tabIndex ) {            
46329             this.TabIndex = oTextarea.tabIndex ;
46330         }
46331         
46332         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46333         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46334         this.frame = Roo.get(this.getId() + '___Frame')
46335     },
46336     
46337     _getConfigHtml : function()
46338     {
46339         var sConfig = '' ;
46340
46341         for ( var o in this.fckconfig ) {
46342             sConfig += sConfig.length > 0  ? '&amp;' : '';
46343             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46344         }
46345
46346         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46347     },
46348     
46349     
46350     _getIFrameHtml : function()
46351     {
46352         var sFile = 'fckeditor.html' ;
46353         /* no idea what this is about..
46354         try
46355         {
46356             if ( (/fcksource=true/i).test( window.top.location.search ) )
46357                 sFile = 'fckeditor.original.html' ;
46358         }
46359         catch (e) { 
46360         */
46361
46362         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46363         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46364         
46365         
46366         var html = '<iframe id="' + this.getId() +
46367             '___Frame" src="' + sLink +
46368             '" width="' + this.width +
46369             '" height="' + this.height + '"' +
46370             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46371             ' frameborder="0" scrolling="no"></iframe>' ;
46372
46373         return html ;
46374     },
46375     
46376     _insertHtmlBefore : function( html, element )
46377     {
46378         if ( element.insertAdjacentHTML )       {
46379             // IE
46380             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46381         } else { // Gecko
46382             var oRange = document.createRange() ;
46383             oRange.setStartBefore( element ) ;
46384             var oFragment = oRange.createContextualFragment( html );
46385             element.parentNode.insertBefore( oFragment, element ) ;
46386         }
46387     }
46388     
46389     
46390   
46391     
46392     
46393     
46394     
46395
46396 });
46397
46398 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46399
46400 function FCKeditor_OnComplete(editorInstance){
46401     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46402     f.fckEditor = editorInstance;
46403     //console.log("loaded");
46404     f.fireEvent('editorinit', f, editorInstance);
46405
46406   
46407
46408  
46409
46410
46411
46412
46413
46414
46415
46416
46417
46418
46419
46420
46421
46422
46423
46424 //<script type="text/javascript">
46425 /**
46426  * @class Roo.form.GridField
46427  * @extends Roo.form.Field
46428  * Embed a grid (or editable grid into a form)
46429  * STATUS ALPHA
46430  * 
46431  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46432  * it needs 
46433  * xgrid.store = Roo.data.Store
46434  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46435  * xgrid.store.reader = Roo.data.JsonReader 
46436  * 
46437  * 
46438  * @constructor
46439  * Creates a new GridField
46440  * @param {Object} config Configuration options
46441  */
46442 Roo.form.GridField = function(config){
46443     Roo.form.GridField.superclass.constructor.call(this, config);
46444      
46445 };
46446
46447 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46448     /**
46449      * @cfg {Number} width  - used to restrict width of grid..
46450      */
46451     width : 100,
46452     /**
46453      * @cfg {Number} height - used to restrict height of grid..
46454      */
46455     height : 50,
46456      /**
46457      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46458          * 
46459          *}
46460      */
46461     xgrid : false, 
46462     /**
46463      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46464      * {tag: "input", type: "checkbox", autocomplete: "off"})
46465      */
46466    // defaultAutoCreate : { tag: 'div' },
46467     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46468     /**
46469      * @cfg {String} addTitle Text to include for adding a title.
46470      */
46471     addTitle : false,
46472     //
46473     onResize : function(){
46474         Roo.form.Field.superclass.onResize.apply(this, arguments);
46475     },
46476
46477     initEvents : function(){
46478         // Roo.form.Checkbox.superclass.initEvents.call(this);
46479         // has no events...
46480        
46481     },
46482
46483
46484     getResizeEl : function(){
46485         return this.wrap;
46486     },
46487
46488     getPositionEl : function(){
46489         return this.wrap;
46490     },
46491
46492     // private
46493     onRender : function(ct, position){
46494         
46495         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46496         var style = this.style;
46497         delete this.style;
46498         
46499         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46500         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46501         this.viewEl = this.wrap.createChild({ tag: 'div' });
46502         if (style) {
46503             this.viewEl.applyStyles(style);
46504         }
46505         if (this.width) {
46506             this.viewEl.setWidth(this.width);
46507         }
46508         if (this.height) {
46509             this.viewEl.setHeight(this.height);
46510         }
46511         //if(this.inputValue !== undefined){
46512         //this.setValue(this.value);
46513         
46514         
46515         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46516         
46517         
46518         this.grid.render();
46519         this.grid.getDataSource().on('remove', this.refreshValue, this);
46520         this.grid.getDataSource().on('update', this.refreshValue, this);
46521         this.grid.on('afteredit', this.refreshValue, this);
46522  
46523     },
46524      
46525     
46526     /**
46527      * Sets the value of the item. 
46528      * @param {String} either an object  or a string..
46529      */
46530     setValue : function(v){
46531         //this.value = v;
46532         v = v || []; // empty set..
46533         // this does not seem smart - it really only affects memoryproxy grids..
46534         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46535             var ds = this.grid.getDataSource();
46536             // assumes a json reader..
46537             var data = {}
46538             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46539             ds.loadData( data);
46540         }
46541         // clear selection so it does not get stale.
46542         if (this.grid.sm) { 
46543             this.grid.sm.clearSelections();
46544         }
46545         
46546         Roo.form.GridField.superclass.setValue.call(this, v);
46547         this.refreshValue();
46548         // should load data in the grid really....
46549     },
46550     
46551     // private
46552     refreshValue: function() {
46553          var val = [];
46554         this.grid.getDataSource().each(function(r) {
46555             val.push(r.data);
46556         });
46557         this.el.dom.value = Roo.encode(val);
46558     }
46559     
46560      
46561     
46562     
46563 });/*
46564  * Based on:
46565  * Ext JS Library 1.1.1
46566  * Copyright(c) 2006-2007, Ext JS, LLC.
46567  *
46568  * Originally Released Under LGPL - original licence link has changed is not relivant.
46569  *
46570  * Fork - LGPL
46571  * <script type="text/javascript">
46572  */
46573 /**
46574  * @class Roo.form.DisplayField
46575  * @extends Roo.form.Field
46576  * A generic Field to display non-editable data.
46577  * @constructor
46578  * Creates a new Display Field item.
46579  * @param {Object} config Configuration options
46580  */
46581 Roo.form.DisplayField = function(config){
46582     Roo.form.DisplayField.superclass.constructor.call(this, config);
46583     
46584 };
46585
46586 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46587     inputType:      'hidden',
46588     allowBlank:     true,
46589     readOnly:         true,
46590     
46591  
46592     /**
46593      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46594      */
46595     focusClass : undefined,
46596     /**
46597      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46598      */
46599     fieldClass: 'x-form-field',
46600     
46601      /**
46602      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46603      */
46604     valueRenderer: undefined,
46605     
46606     width: 100,
46607     /**
46608      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46609      * {tag: "input", type: "checkbox", autocomplete: "off"})
46610      */
46611      
46612  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46613
46614     onResize : function(){
46615         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46616         
46617     },
46618
46619     initEvents : function(){
46620         // Roo.form.Checkbox.superclass.initEvents.call(this);
46621         // has no events...
46622        
46623     },
46624
46625
46626     getResizeEl : function(){
46627         return this.wrap;
46628     },
46629
46630     getPositionEl : function(){
46631         return this.wrap;
46632     },
46633
46634     // private
46635     onRender : function(ct, position){
46636         
46637         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46638         //if(this.inputValue !== undefined){
46639         this.wrap = this.el.wrap();
46640         
46641         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46642         
46643         if (this.bodyStyle) {
46644             this.viewEl.applyStyles(this.bodyStyle);
46645         }
46646         //this.viewEl.setStyle('padding', '2px');
46647         
46648         this.setValue(this.value);
46649         
46650     },
46651 /*
46652     // private
46653     initValue : Roo.emptyFn,
46654
46655   */
46656
46657         // private
46658     onClick : function(){
46659         
46660     },
46661
46662     /**
46663      * Sets the checked state of the checkbox.
46664      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46665      */
46666     setValue : function(v){
46667         this.value = v;
46668         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46669         // this might be called before we have a dom element..
46670         if (!this.viewEl) {
46671             return;
46672         }
46673         this.viewEl.dom.innerHTML = html;
46674         Roo.form.DisplayField.superclass.setValue.call(this, v);
46675
46676     }
46677 });/*
46678  * 
46679  * Licence- LGPL
46680  * 
46681  */
46682
46683 /**
46684  * @class Roo.form.DayPicker
46685  * @extends Roo.form.Field
46686  * A Day picker show [M] [T] [W] ....
46687  * @constructor
46688  * Creates a new Day Picker
46689  * @param {Object} config Configuration options
46690  */
46691 Roo.form.DayPicker= function(config){
46692     Roo.form.DayPicker.superclass.constructor.call(this, config);
46693      
46694 };
46695
46696 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46697     /**
46698      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46699      */
46700     focusClass : undefined,
46701     /**
46702      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46703      */
46704     fieldClass: "x-form-field",
46705    
46706     /**
46707      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46708      * {tag: "input", type: "checkbox", autocomplete: "off"})
46709      */
46710     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46711     
46712    
46713     actionMode : 'viewEl', 
46714     //
46715     // private
46716  
46717     inputType : 'hidden',
46718     
46719      
46720     inputElement: false, // real input element?
46721     basedOn: false, // ????
46722     
46723     isFormField: true, // not sure where this is needed!!!!
46724
46725     onResize : function(){
46726         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46727         if(!this.boxLabel){
46728             this.el.alignTo(this.wrap, 'c-c');
46729         }
46730     },
46731
46732     initEvents : function(){
46733         Roo.form.Checkbox.superclass.initEvents.call(this);
46734         this.el.on("click", this.onClick,  this);
46735         this.el.on("change", this.onClick,  this);
46736     },
46737
46738
46739     getResizeEl : function(){
46740         return this.wrap;
46741     },
46742
46743     getPositionEl : function(){
46744         return this.wrap;
46745     },
46746
46747     
46748     // private
46749     onRender : function(ct, position){
46750         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46751        
46752         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46753         
46754         var r1 = '<table><tr>';
46755         var r2 = '<tr class="x-form-daypick-icons">';
46756         for (var i=0; i < 7; i++) {
46757             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46758             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46759         }
46760         
46761         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46762         viewEl.select('img').on('click', this.onClick, this);
46763         this.viewEl = viewEl;   
46764         
46765         
46766         // this will not work on Chrome!!!
46767         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46768         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46769         
46770         
46771           
46772
46773     },
46774
46775     // private
46776     initValue : Roo.emptyFn,
46777
46778     /**
46779      * Returns the checked state of the checkbox.
46780      * @return {Boolean} True if checked, else false
46781      */
46782     getValue : function(){
46783         return this.el.dom.value;
46784         
46785     },
46786
46787         // private
46788     onClick : function(e){ 
46789         //this.setChecked(!this.checked);
46790         Roo.get(e.target).toggleClass('x-menu-item-checked');
46791         this.refreshValue();
46792         //if(this.el.dom.checked != this.checked){
46793         //    this.setValue(this.el.dom.checked);
46794        // }
46795     },
46796     
46797     // private
46798     refreshValue : function()
46799     {
46800         var val = '';
46801         this.viewEl.select('img',true).each(function(e,i,n)  {
46802             val += e.is(".x-menu-item-checked") ? String(n) : '';
46803         });
46804         this.setValue(val, true);
46805     },
46806
46807     /**
46808      * Sets the checked state of the checkbox.
46809      * On is always based on a string comparison between inputValue and the param.
46810      * @param {Boolean/String} value - the value to set 
46811      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46812      */
46813     setValue : function(v,suppressEvent){
46814         if (!this.el.dom) {
46815             return;
46816         }
46817         var old = this.el.dom.value ;
46818         this.el.dom.value = v;
46819         if (suppressEvent) {
46820             return ;
46821         }
46822          
46823         // update display..
46824         this.viewEl.select('img',true).each(function(e,i,n)  {
46825             
46826             var on = e.is(".x-menu-item-checked");
46827             var newv = v.indexOf(String(n)) > -1;
46828             if (on != newv) {
46829                 e.toggleClass('x-menu-item-checked');
46830             }
46831             
46832         });
46833         
46834         
46835         this.fireEvent('change', this, v, old);
46836         
46837         
46838     },
46839    
46840     // handle setting of hidden value by some other method!!?!?
46841     setFromHidden: function()
46842     {
46843         if(!this.el){
46844             return;
46845         }
46846         //console.log("SET FROM HIDDEN");
46847         //alert('setFrom hidden');
46848         this.setValue(this.el.dom.value);
46849     },
46850     
46851     onDestroy : function()
46852     {
46853         if(this.viewEl){
46854             Roo.get(this.viewEl).remove();
46855         }
46856          
46857         Roo.form.DayPicker.superclass.onDestroy.call(this);
46858     }
46859
46860 });/*
46861  * RooJS Library 1.1.1
46862  * Copyright(c) 2008-2011  Alan Knowles
46863  *
46864  * License - LGPL
46865  */
46866  
46867
46868 /**
46869  * @class Roo.form.ComboCheck
46870  * @extends Roo.form.ComboBox
46871  * A combobox for multiple select items.
46872  *
46873  * FIXME - could do with a reset button..
46874  * 
46875  * @constructor
46876  * Create a new ComboCheck
46877  * @param {Object} config Configuration options
46878  */
46879 Roo.form.ComboCheck = function(config){
46880     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46881     // should verify some data...
46882     // like
46883     // hiddenName = required..
46884     // displayField = required
46885     // valudField == required
46886     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46887     var _t = this;
46888     Roo.each(req, function(e) {
46889         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46890             throw "Roo.form.ComboCheck : missing value for: " + e;
46891         }
46892     });
46893     
46894     
46895 };
46896
46897 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46898      
46899      
46900     editable : false,
46901      
46902     selectedClass: 'x-menu-item-checked', 
46903     
46904     // private
46905     onRender : function(ct, position){
46906         var _t = this;
46907         
46908         
46909         
46910         if(!this.tpl){
46911             var cls = 'x-combo-list';
46912
46913             
46914             this.tpl =  new Roo.Template({
46915                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46916                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46917                    '<span>{' + this.displayField + '}</span>' +
46918                     '</div>' 
46919                 
46920             });
46921         }
46922  
46923         
46924         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46925         this.view.singleSelect = false;
46926         this.view.multiSelect = true;
46927         this.view.toggleSelect = true;
46928         this.pageTb.add(new Roo.Toolbar.Fill(), {
46929             
46930             text: 'Done',
46931             handler: function()
46932             {
46933                 _t.collapse();
46934             }
46935         });
46936     },
46937     
46938     onViewOver : function(e, t){
46939         // do nothing...
46940         return;
46941         
46942     },
46943     
46944     onViewClick : function(doFocus,index){
46945         return;
46946         
46947     },
46948     select: function () {
46949         //Roo.log("SELECT CALLED");
46950     },
46951      
46952     selectByValue : function(xv, scrollIntoView){
46953         var ar = this.getValueArray();
46954         var sels = [];
46955         
46956         Roo.each(ar, function(v) {
46957             if(v === undefined || v === null){
46958                 return;
46959             }
46960             var r = this.findRecord(this.valueField, v);
46961             if(r){
46962                 sels.push(this.store.indexOf(r))
46963                 
46964             }
46965         },this);
46966         this.view.select(sels);
46967         return false;
46968     },
46969     
46970     
46971     
46972     onSelect : function(record, index){
46973        // Roo.log("onselect Called");
46974        // this is only called by the clear button now..
46975         this.view.clearSelections();
46976         this.setValue('[]');
46977         if (this.value != this.valueBefore) {
46978             this.fireEvent('change', this, this.value, this.valueBefore);
46979             this.valueBefore = this.value;
46980         }
46981     },
46982     getValueArray : function()
46983     {
46984         var ar = [] ;
46985         
46986         try {
46987             //Roo.log(this.value);
46988             if (typeof(this.value) == 'undefined') {
46989                 return [];
46990             }
46991             var ar = Roo.decode(this.value);
46992             return  ar instanceof Array ? ar : []; //?? valid?
46993             
46994         } catch(e) {
46995             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46996             return [];
46997         }
46998          
46999     },
47000     expand : function ()
47001     {
47002         
47003         Roo.form.ComboCheck.superclass.expand.call(this);
47004         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47005         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47006         
47007
47008     },
47009     
47010     collapse : function(){
47011         Roo.form.ComboCheck.superclass.collapse.call(this);
47012         var sl = this.view.getSelectedIndexes();
47013         var st = this.store;
47014         var nv = [];
47015         var tv = [];
47016         var r;
47017         Roo.each(sl, function(i) {
47018             r = st.getAt(i);
47019             nv.push(r.get(this.valueField));
47020         },this);
47021         this.setValue(Roo.encode(nv));
47022         if (this.value != this.valueBefore) {
47023
47024             this.fireEvent('change', this, this.value, this.valueBefore);
47025             this.valueBefore = this.value;
47026         }
47027         
47028     },
47029     
47030     setValue : function(v){
47031         // Roo.log(v);
47032         this.value = v;
47033         
47034         var vals = this.getValueArray();
47035         var tv = [];
47036         Roo.each(vals, function(k) {
47037             var r = this.findRecord(this.valueField, k);
47038             if(r){
47039                 tv.push(r.data[this.displayField]);
47040             }else if(this.valueNotFoundText !== undefined){
47041                 tv.push( this.valueNotFoundText );
47042             }
47043         },this);
47044        // Roo.log(tv);
47045         
47046         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47047         this.hiddenField.value = v;
47048         this.value = v;
47049     }
47050     
47051 });/*
47052  * Based on:
47053  * Ext JS Library 1.1.1
47054  * Copyright(c) 2006-2007, Ext JS, LLC.
47055  *
47056  * Originally Released Under LGPL - original licence link has changed is not relivant.
47057  *
47058  * Fork - LGPL
47059  * <script type="text/javascript">
47060  */
47061  
47062 /**
47063  * @class Roo.form.Signature
47064  * @extends Roo.form.Field
47065  * Signature field.  
47066  * @constructor
47067  * 
47068  * @param {Object} config Configuration options
47069  */
47070
47071 Roo.form.Signature = function(config){
47072     Roo.form.Signature.superclass.constructor.call(this, config);
47073     
47074     this.addEvents({// not in used??
47075          /**
47076          * @event confirm
47077          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47078              * @param {Roo.form.Signature} combo This combo box
47079              */
47080         'confirm' : true,
47081         /**
47082          * @event reset
47083          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47084              * @param {Roo.form.ComboBox} combo This combo box
47085              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47086              */
47087         'reset' : true
47088     });
47089 };
47090
47091 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47092     /**
47093      * @cfg {Object} labels Label to use when rendering a form.
47094      * defaults to 
47095      * labels : { 
47096      *      clear : "Clear",
47097      *      confirm : "Confirm"
47098      *  }
47099      */
47100     labels : { 
47101         clear : "Clear",
47102         confirm : "Confirm"
47103     },
47104     /**
47105      * @cfg {Number} width The signature panel width (defaults to 300)
47106      */
47107     width: 300,
47108     /**
47109      * @cfg {Number} height The signature panel height (defaults to 100)
47110      */
47111     height : 100,
47112     /**
47113      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47114      */
47115     allowBlank : false,
47116     
47117     //private
47118     // {Object} signPanel The signature SVG panel element (defaults to {})
47119     signPanel : {},
47120     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47121     isMouseDown : false,
47122     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47123     isConfirmed : false,
47124     // {String} signatureTmp SVG mapping string (defaults to empty string)
47125     signatureTmp : '',
47126     
47127     
47128     defaultAutoCreate : { // modified by initCompnoent..
47129         tag: "input",
47130         type:"hidden"
47131     },
47132
47133     // private
47134     onRender : function(ct, position){
47135         
47136         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47137         
47138         this.wrap = this.el.wrap({
47139             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47140         });
47141         
47142         this.createToolbar(this);
47143         this.signPanel = this.wrap.createChild({
47144                 tag: 'div',
47145                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47146             }, this.el
47147         );
47148             
47149         this.svgID = Roo.id();
47150         this.svgEl = this.signPanel.createChild({
47151               xmlns : 'http://www.w3.org/2000/svg',
47152               tag : 'svg',
47153               id : this.svgID + "-svg",
47154               width: this.width,
47155               height: this.height,
47156               viewBox: '0 0 '+this.width+' '+this.height,
47157               cn : [
47158                 {
47159                     tag: "rect",
47160                     id: this.svgID + "-svg-r",
47161                     width: this.width,
47162                     height: this.height,
47163                     fill: "#ffa"
47164                 },
47165                 {
47166                     tag: "line",
47167                     id: this.svgID + "-svg-l",
47168                     x1: "0", // start
47169                     y1: (this.height*0.8), // start set the line in 80% of height
47170                     x2: this.width, // end
47171                     y2: (this.height*0.8), // end set the line in 80% of height
47172                     'stroke': "#666",
47173                     'stroke-width': "1",
47174                     'stroke-dasharray': "3",
47175                     'shape-rendering': "crispEdges",
47176                     'pointer-events': "none"
47177                 },
47178                 {
47179                     tag: "path",
47180                     id: this.svgID + "-svg-p",
47181                     'stroke': "navy",
47182                     'stroke-width': "3",
47183                     'fill': "none",
47184                     'pointer-events': 'none'
47185                 }
47186               ]
47187         });
47188         this.createSVG();
47189         this.svgBox = this.svgEl.dom.getScreenCTM();
47190     },
47191     createSVG : function(){ 
47192         var svg = this.signPanel;
47193         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47194         var t = this;
47195
47196         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47197         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47198         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47199         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47200         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47201         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47202         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47203         
47204     },
47205     isTouchEvent : function(e){
47206         return e.type.match(/^touch/);
47207     },
47208     getCoords : function (e) {
47209         var pt    = this.svgEl.dom.createSVGPoint();
47210         pt.x = e.clientX; 
47211         pt.y = e.clientY;
47212         if (this.isTouchEvent(e)) {
47213             pt.x =  e.targetTouches[0].clientX 
47214             pt.y = e.targetTouches[0].clientY;
47215         }
47216         var a = this.svgEl.dom.getScreenCTM();
47217         var b = a.inverse();
47218         var mx = pt.matrixTransform(b);
47219         return mx.x + ',' + mx.y;
47220     },
47221     //mouse event headler 
47222     down : function (e) {
47223         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47224         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47225         
47226         this.isMouseDown = true;
47227         
47228         e.preventDefault();
47229     },
47230     move : function (e) {
47231         if (this.isMouseDown) {
47232             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47233             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47234         }
47235         
47236         e.preventDefault();
47237     },
47238     up : function (e) {
47239         this.isMouseDown = false;
47240         var sp = this.signatureTmp.split(' ');
47241         
47242         if(sp.length > 1){
47243             if(!sp[sp.length-2].match(/^L/)){
47244                 sp.pop();
47245                 sp.pop();
47246                 sp.push("");
47247                 this.signatureTmp = sp.join(" ");
47248             }
47249         }
47250         if(this.getValue() != this.signatureTmp){
47251             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47252             this.isConfirmed = false;
47253         }
47254         e.preventDefault();
47255     },
47256     
47257     /**
47258      * Protected method that will not generally be called directly. It
47259      * is called when the editor creates its toolbar. Override this method if you need to
47260      * add custom toolbar buttons.
47261      * @param {HtmlEditor} editor
47262      */
47263     createToolbar : function(editor){
47264          function btn(id, toggle, handler){
47265             var xid = fid + '-'+ id ;
47266             return {
47267                 id : xid,
47268                 cmd : id,
47269                 cls : 'x-btn-icon x-edit-'+id,
47270                 enableToggle:toggle !== false,
47271                 scope: editor, // was editor...
47272                 handler:handler||editor.relayBtnCmd,
47273                 clickEvent:'mousedown',
47274                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47275                 tabIndex:-1
47276             };
47277         }
47278         
47279         
47280         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47281         this.tb = tb;
47282         this.tb.add(
47283            {
47284                 cls : ' x-signature-btn x-signature-'+id,
47285                 scope: editor, // was editor...
47286                 handler: this.reset,
47287                 clickEvent:'mousedown',
47288                 text: this.labels.clear
47289             },
47290             {
47291                  xtype : 'Fill',
47292                  xns: Roo.Toolbar
47293             }, 
47294             {
47295                 cls : '  x-signature-btn x-signature-'+id,
47296                 scope: editor, // was editor...
47297                 handler: this.confirmHandler,
47298                 clickEvent:'mousedown',
47299                 text: this.labels.confirm
47300             }
47301         );
47302     
47303     },
47304     //public
47305     /**
47306      * when user is clicked confirm then show this image.....
47307      * 
47308      * @return {String} Image Data URI
47309      */
47310     getImageDataURI : function(){
47311         var svg = this.svgEl.dom.parentNode.innerHTML;
47312         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47313         return src; 
47314     },
47315     /**
47316      * 
47317      * @return {Boolean} this.isConfirmed
47318      */
47319     getConfirmed : function(){
47320         return this.isConfirmed;
47321     },
47322     /**
47323      * 
47324      * @return {Number} this.width
47325      */
47326     getWidth : function(){
47327         return this.width;
47328     },
47329     /**
47330      * 
47331      * @return {Number} this.height
47332      */
47333     getHeight : function(){
47334         return this.height;
47335     },
47336     // private
47337     getSignature : function(){
47338         return this.signatureTmp;
47339     },
47340     // private
47341     reset : function(){
47342         this.signatureTmp = '';
47343         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47344         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47345         this.isConfirmed = false;
47346         Roo.form.Signature.superclass.reset.call(this);
47347     },
47348     setSignature : function(s){
47349         this.signatureTmp = s;
47350         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47351         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47352         this.setValue(s);
47353         this.isConfirmed = false;
47354         Roo.form.Signature.superclass.reset.call(this);
47355     }, 
47356     test : function(){
47357 //        Roo.log(this.signPanel.dom.contentWindow.up())
47358     },
47359     //private
47360     setConfirmed : function(){
47361         
47362         
47363         
47364 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47365     },
47366     // private
47367     confirmHandler : function(){
47368         if(!this.getSignature()){
47369             return;
47370         }
47371         
47372         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47373         this.setValue(this.getSignature());
47374         this.isConfirmed = true;
47375         
47376         this.fireEvent('confirm', this);
47377     },
47378     // private
47379     // Subclasses should provide the validation implementation by overriding this
47380     validateValue : function(value){
47381         if(this.allowBlank){
47382             return true;
47383         }
47384         
47385         if(this.isConfirmed){
47386             return true;
47387         }
47388         return false;
47389     }
47390 });/*
47391  * Based on:
47392  * Ext JS Library 1.1.1
47393  * Copyright(c) 2006-2007, Ext JS, LLC.
47394  *
47395  * Originally Released Under LGPL - original licence link has changed is not relivant.
47396  *
47397  * Fork - LGPL
47398  * <script type="text/javascript">
47399  */
47400  
47401
47402 /**
47403  * @class Roo.form.ComboBox
47404  * @extends Roo.form.TriggerField
47405  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47406  * @constructor
47407  * Create a new ComboBox.
47408  * @param {Object} config Configuration options
47409  */
47410 Roo.form.Select = function(config){
47411     Roo.form.Select.superclass.constructor.call(this, config);
47412      
47413 };
47414
47415 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47416     /**
47417      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47418      */
47419     /**
47420      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47421      * rendering into an Roo.Editor, defaults to false)
47422      */
47423     /**
47424      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47425      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47426      */
47427     /**
47428      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47429      */
47430     /**
47431      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47432      * the dropdown list (defaults to undefined, with no header element)
47433      */
47434
47435      /**
47436      * @cfg {String/Roo.Template} tpl The template to use to render the output
47437      */
47438      
47439     // private
47440     defaultAutoCreate : {tag: "select"  },
47441     /**
47442      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47443      */
47444     listWidth: undefined,
47445     /**
47446      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47447      * mode = 'remote' or 'text' if mode = 'local')
47448      */
47449     displayField: undefined,
47450     /**
47451      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47452      * mode = 'remote' or 'value' if mode = 'local'). 
47453      * Note: use of a valueField requires the user make a selection
47454      * in order for a value to be mapped.
47455      */
47456     valueField: undefined,
47457     
47458     
47459     /**
47460      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47461      * field's data value (defaults to the underlying DOM element's name)
47462      */
47463     hiddenName: undefined,
47464     /**
47465      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47466      */
47467     listClass: '',
47468     /**
47469      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47470      */
47471     selectedClass: 'x-combo-selected',
47472     /**
47473      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47474      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47475      * which displays a downward arrow icon).
47476      */
47477     triggerClass : 'x-form-arrow-trigger',
47478     /**
47479      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47480      */
47481     shadow:'sides',
47482     /**
47483      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47484      * anchor positions (defaults to 'tl-bl')
47485      */
47486     listAlign: 'tl-bl?',
47487     /**
47488      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47489      */
47490     maxHeight: 300,
47491     /**
47492      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47493      * query specified by the allQuery config option (defaults to 'query')
47494      */
47495     triggerAction: 'query',
47496     /**
47497      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47498      * (defaults to 4, does not apply if editable = false)
47499      */
47500     minChars : 4,
47501     /**
47502      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47503      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47504      */
47505     typeAhead: false,
47506     /**
47507      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47508      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47509      */
47510     queryDelay: 500,
47511     /**
47512      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47513      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47514      */
47515     pageSize: 0,
47516     /**
47517      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47518      * when editable = true (defaults to false)
47519      */
47520     selectOnFocus:false,
47521     /**
47522      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47523      */
47524     queryParam: 'query',
47525     /**
47526      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47527      * when mode = 'remote' (defaults to 'Loading...')
47528      */
47529     loadingText: 'Loading...',
47530     /**
47531      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47532      */
47533     resizable: false,
47534     /**
47535      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47536      */
47537     handleHeight : 8,
47538     /**
47539      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47540      * traditional select (defaults to true)
47541      */
47542     editable: true,
47543     /**
47544      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47545      */
47546     allQuery: '',
47547     /**
47548      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47549      */
47550     mode: 'remote',
47551     /**
47552      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47553      * listWidth has a higher value)
47554      */
47555     minListWidth : 70,
47556     /**
47557      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47558      * allow the user to set arbitrary text into the field (defaults to false)
47559      */
47560     forceSelection:false,
47561     /**
47562      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47563      * if typeAhead = true (defaults to 250)
47564      */
47565     typeAheadDelay : 250,
47566     /**
47567      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47568      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47569      */
47570     valueNotFoundText : undefined,
47571     
47572     /**
47573      * @cfg {String} defaultValue The value displayed after loading the store.
47574      */
47575     defaultValue: '',
47576     
47577     /**
47578      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47579      */
47580     blockFocus : false,
47581     
47582     /**
47583      * @cfg {Boolean} disableClear Disable showing of clear button.
47584      */
47585     disableClear : false,
47586     /**
47587      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47588      */
47589     alwaysQuery : false,
47590     
47591     //private
47592     addicon : false,
47593     editicon: false,
47594     
47595     // element that contains real text value.. (when hidden is used..)
47596      
47597     // private
47598     onRender : function(ct, position){
47599         Roo.form.Field.prototype.onRender.call(this, ct, position);
47600         
47601         if(this.store){
47602             this.store.on('beforeload', this.onBeforeLoad, this);
47603             this.store.on('load', this.onLoad, this);
47604             this.store.on('loadexception', this.onLoadException, this);
47605             this.store.load({});
47606         }
47607         
47608         
47609         
47610     },
47611
47612     // private
47613     initEvents : function(){
47614         //Roo.form.ComboBox.superclass.initEvents.call(this);
47615  
47616     },
47617
47618     onDestroy : function(){
47619        
47620         if(this.store){
47621             this.store.un('beforeload', this.onBeforeLoad, this);
47622             this.store.un('load', this.onLoad, this);
47623             this.store.un('loadexception', this.onLoadException, this);
47624         }
47625         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47626     },
47627
47628     // private
47629     fireKey : function(e){
47630         if(e.isNavKeyPress() && !this.list.isVisible()){
47631             this.fireEvent("specialkey", this, e);
47632         }
47633     },
47634
47635     // private
47636     onResize: function(w, h){
47637         
47638         return; 
47639     
47640         
47641     },
47642
47643     /**
47644      * Allow or prevent the user from directly editing the field text.  If false is passed,
47645      * the user will only be able to select from the items defined in the dropdown list.  This method
47646      * is the runtime equivalent of setting the 'editable' config option at config time.
47647      * @param {Boolean} value True to allow the user to directly edit the field text
47648      */
47649     setEditable : function(value){
47650          
47651     },
47652
47653     // private
47654     onBeforeLoad : function(){
47655         
47656         Roo.log("Select before load");
47657         return;
47658     
47659         this.innerList.update(this.loadingText ?
47660                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47661         //this.restrictHeight();
47662         this.selectedIndex = -1;
47663     },
47664
47665     // private
47666     onLoad : function(){
47667
47668     
47669         var dom = this.el.dom;
47670         dom.innerHTML = '';
47671          var od = dom.ownerDocument;
47672          
47673         if (this.emptyText) {
47674             var op = od.createElement('option');
47675             op.setAttribute('value', '');
47676             op.innerHTML = String.format('{0}', this.emptyText);
47677             dom.appendChild(op);
47678         }
47679         if(this.store.getCount() > 0){
47680            
47681             var vf = this.valueField;
47682             var df = this.displayField;
47683             this.store.data.each(function(r) {
47684                 // which colmsn to use... testing - cdoe / title..
47685                 var op = od.createElement('option');
47686                 op.setAttribute('value', r.data[vf]);
47687                 op.innerHTML = String.format('{0}', r.data[df]);
47688                 dom.appendChild(op);
47689             });
47690             if (typeof(this.defaultValue != 'undefined')) {
47691                 this.setValue(this.defaultValue);
47692             }
47693             
47694              
47695         }else{
47696             //this.onEmptyResults();
47697         }
47698         //this.el.focus();
47699     },
47700     // private
47701     onLoadException : function()
47702     {
47703         dom.innerHTML = '';
47704             
47705         Roo.log("Select on load exception");
47706         return;
47707     
47708         this.collapse();
47709         Roo.log(this.store.reader.jsonData);
47710         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47711             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47712         }
47713         
47714         
47715     },
47716     // private
47717     onTypeAhead : function(){
47718          
47719     },
47720
47721     // private
47722     onSelect : function(record, index){
47723         Roo.log('on select?');
47724         return;
47725         if(this.fireEvent('beforeselect', this, record, index) !== false){
47726             this.setFromData(index > -1 ? record.data : false);
47727             this.collapse();
47728             this.fireEvent('select', this, record, index);
47729         }
47730     },
47731
47732     /**
47733      * Returns the currently selected field value or empty string if no value is set.
47734      * @return {String} value The selected value
47735      */
47736     getValue : function(){
47737         var dom = this.el.dom;
47738         this.value = dom.options[dom.selectedIndex].value;
47739         return this.value;
47740         
47741     },
47742
47743     /**
47744      * Clears any text/value currently set in the field
47745      */
47746     clearValue : function(){
47747         this.value = '';
47748         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47749         
47750     },
47751
47752     /**
47753      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47754      * will be displayed in the field.  If the value does not match the data value of an existing item,
47755      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47756      * Otherwise the field will be blank (although the value will still be set).
47757      * @param {String} value The value to match
47758      */
47759     setValue : function(v){
47760         var d = this.el.dom;
47761         for (var i =0; i < d.options.length;i++) {
47762             if (v == d.options[i].value) {
47763                 d.selectedIndex = i;
47764                 this.value = v;
47765                 return;
47766             }
47767         }
47768         this.clearValue();
47769     },
47770     /**
47771      * @property {Object} the last set data for the element
47772      */
47773     
47774     lastData : false,
47775     /**
47776      * Sets the value of the field based on a object which is related to the record format for the store.
47777      * @param {Object} value the value to set as. or false on reset?
47778      */
47779     setFromData : function(o){
47780         Roo.log('setfrom data?');
47781          
47782         
47783         
47784     },
47785     // private
47786     reset : function(){
47787         this.clearValue();
47788     },
47789     // private
47790     findRecord : function(prop, value){
47791         
47792         return false;
47793     
47794         var record;
47795         if(this.store.getCount() > 0){
47796             this.store.each(function(r){
47797                 if(r.data[prop] == value){
47798                     record = r;
47799                     return false;
47800                 }
47801                 return true;
47802             });
47803         }
47804         return record;
47805     },
47806     
47807     getName: function()
47808     {
47809         // returns hidden if it's set..
47810         if (!this.rendered) {return ''};
47811         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47812         
47813     },
47814      
47815
47816     
47817
47818     // private
47819     onEmptyResults : function(){
47820         Roo.log('empty results');
47821         //this.collapse();
47822     },
47823
47824     /**
47825      * Returns true if the dropdown list is expanded, else false.
47826      */
47827     isExpanded : function(){
47828         return false;
47829     },
47830
47831     /**
47832      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47833      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47834      * @param {String} value The data value of the item to select
47835      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47836      * selected item if it is not currently in view (defaults to true)
47837      * @return {Boolean} True if the value matched an item in the list, else false
47838      */
47839     selectByValue : function(v, scrollIntoView){
47840         Roo.log('select By Value');
47841         return false;
47842     
47843         if(v !== undefined && v !== null){
47844             var r = this.findRecord(this.valueField || this.displayField, v);
47845             if(r){
47846                 this.select(this.store.indexOf(r), scrollIntoView);
47847                 return true;
47848             }
47849         }
47850         return false;
47851     },
47852
47853     /**
47854      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47855      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47856      * @param {Number} index The zero-based index of the list item to select
47857      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47858      * selected item if it is not currently in view (defaults to true)
47859      */
47860     select : function(index, scrollIntoView){
47861         Roo.log('select ');
47862         return  ;
47863         
47864         this.selectedIndex = index;
47865         this.view.select(index);
47866         if(scrollIntoView !== false){
47867             var el = this.view.getNode(index);
47868             if(el){
47869                 this.innerList.scrollChildIntoView(el, false);
47870             }
47871         }
47872     },
47873
47874       
47875
47876     // private
47877     validateBlur : function(){
47878         
47879         return;
47880         
47881     },
47882
47883     // private
47884     initQuery : function(){
47885         this.doQuery(this.getRawValue());
47886     },
47887
47888     // private
47889     doForce : function(){
47890         if(this.el.dom.value.length > 0){
47891             this.el.dom.value =
47892                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47893              
47894         }
47895     },
47896
47897     /**
47898      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47899      * query allowing the query action to be canceled if needed.
47900      * @param {String} query The SQL query to execute
47901      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47902      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47903      * saved in the current store (defaults to false)
47904      */
47905     doQuery : function(q, forceAll){
47906         
47907         Roo.log('doQuery?');
47908         if(q === undefined || q === null){
47909             q = '';
47910         }
47911         var qe = {
47912             query: q,
47913             forceAll: forceAll,
47914             combo: this,
47915             cancel:false
47916         };
47917         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47918             return false;
47919         }
47920         q = qe.query;
47921         forceAll = qe.forceAll;
47922         if(forceAll === true || (q.length >= this.minChars)){
47923             if(this.lastQuery != q || this.alwaysQuery){
47924                 this.lastQuery = q;
47925                 if(this.mode == 'local'){
47926                     this.selectedIndex = -1;
47927                     if(forceAll){
47928                         this.store.clearFilter();
47929                     }else{
47930                         this.store.filter(this.displayField, q);
47931                     }
47932                     this.onLoad();
47933                 }else{
47934                     this.store.baseParams[this.queryParam] = q;
47935                     this.store.load({
47936                         params: this.getParams(q)
47937                     });
47938                     this.expand();
47939                 }
47940             }else{
47941                 this.selectedIndex = -1;
47942                 this.onLoad();   
47943             }
47944         }
47945     },
47946
47947     // private
47948     getParams : function(q){
47949         var p = {};
47950         //p[this.queryParam] = q;
47951         if(this.pageSize){
47952             p.start = 0;
47953             p.limit = this.pageSize;
47954         }
47955         return p;
47956     },
47957
47958     /**
47959      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47960      */
47961     collapse : function(){
47962         
47963     },
47964
47965     // private
47966     collapseIf : function(e){
47967         
47968     },
47969
47970     /**
47971      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47972      */
47973     expand : function(){
47974         
47975     } ,
47976
47977     // private
47978      
47979
47980     /** 
47981     * @cfg {Boolean} grow 
47982     * @hide 
47983     */
47984     /** 
47985     * @cfg {Number} growMin 
47986     * @hide 
47987     */
47988     /** 
47989     * @cfg {Number} growMax 
47990     * @hide 
47991     */
47992     /**
47993      * @hide
47994      * @method autoSize
47995      */
47996     
47997     setWidth : function()
47998     {
47999         
48000     },
48001     getResizeEl : function(){
48002         return this.el;
48003     }
48004 });//<script type="text/javasscript">
48005  
48006
48007 /**
48008  * @class Roo.DDView
48009  * A DnD enabled version of Roo.View.
48010  * @param {Element/String} container The Element in which to create the View.
48011  * @param {String} tpl The template string used to create the markup for each element of the View
48012  * @param {Object} config The configuration properties. These include all the config options of
48013  * {@link Roo.View} plus some specific to this class.<br>
48014  * <p>
48015  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48016  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48017  * <p>
48018  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48019 .x-view-drag-insert-above {
48020         border-top:1px dotted #3366cc;
48021 }
48022 .x-view-drag-insert-below {
48023         border-bottom:1px dotted #3366cc;
48024 }
48025 </code></pre>
48026  * 
48027  */
48028  
48029 Roo.DDView = function(container, tpl, config) {
48030     Roo.DDView.superclass.constructor.apply(this, arguments);
48031     this.getEl().setStyle("outline", "0px none");
48032     this.getEl().unselectable();
48033     if (this.dragGroup) {
48034                 this.setDraggable(this.dragGroup.split(","));
48035     }
48036     if (this.dropGroup) {
48037                 this.setDroppable(this.dropGroup.split(","));
48038     }
48039     if (this.deletable) {
48040         this.setDeletable();
48041     }
48042     this.isDirtyFlag = false;
48043         this.addEvents({
48044                 "drop" : true
48045         });
48046 };
48047
48048 Roo.extend(Roo.DDView, Roo.View, {
48049 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48050 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48051 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48052 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48053
48054         isFormField: true,
48055
48056         reset: Roo.emptyFn,
48057         
48058         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48059
48060         validate: function() {
48061                 return true;
48062         },
48063         
48064         destroy: function() {
48065                 this.purgeListeners();
48066                 this.getEl.removeAllListeners();
48067                 this.getEl().remove();
48068                 if (this.dragZone) {
48069                         if (this.dragZone.destroy) {
48070                                 this.dragZone.destroy();
48071                         }
48072                 }
48073                 if (this.dropZone) {
48074                         if (this.dropZone.destroy) {
48075                                 this.dropZone.destroy();
48076                         }
48077                 }
48078         },
48079
48080 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48081         getName: function() {
48082                 return this.name;
48083         },
48084
48085 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48086         setValue: function(v) {
48087                 if (!this.store) {
48088                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48089                 }
48090                 var data = {};
48091                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48092                 this.store.proxy = new Roo.data.MemoryProxy(data);
48093                 this.store.load();
48094         },
48095
48096 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48097         getValue: function() {
48098                 var result = '(';
48099                 this.store.each(function(rec) {
48100                         result += rec.id + ',';
48101                 });
48102                 return result.substr(0, result.length - 1) + ')';
48103         },
48104         
48105         getIds: function() {
48106                 var i = 0, result = new Array(this.store.getCount());
48107                 this.store.each(function(rec) {
48108                         result[i++] = rec.id;
48109                 });
48110                 return result;
48111         },
48112         
48113         isDirty: function() {
48114                 return this.isDirtyFlag;
48115         },
48116
48117 /**
48118  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48119  *      whole Element becomes the target, and this causes the drop gesture to append.
48120  */
48121     getTargetFromEvent : function(e) {
48122                 var target = e.getTarget();
48123                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48124                 target = target.parentNode;
48125                 }
48126                 if (!target) {
48127                         target = this.el.dom.lastChild || this.el.dom;
48128                 }
48129                 return target;
48130     },
48131
48132 /**
48133  *      Create the drag data which consists of an object which has the property "ddel" as
48134  *      the drag proxy element. 
48135  */
48136     getDragData : function(e) {
48137         var target = this.findItemFromChild(e.getTarget());
48138                 if(target) {
48139                         this.handleSelection(e);
48140                         var selNodes = this.getSelectedNodes();
48141             var dragData = {
48142                 source: this,
48143                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48144                 nodes: selNodes,
48145                 records: []
48146                         };
48147                         var selectedIndices = this.getSelectedIndexes();
48148                         for (var i = 0; i < selectedIndices.length; i++) {
48149                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48150                         }
48151                         if (selNodes.length == 1) {
48152                                 dragData.ddel = target.cloneNode(true); // the div element
48153                         } else {
48154                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48155                                 div.className = 'multi-proxy';
48156                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48157                                         div.appendChild(selNodes[i].cloneNode(true));
48158                                 }
48159                                 dragData.ddel = div;
48160                         }
48161             //console.log(dragData)
48162             //console.log(dragData.ddel.innerHTML)
48163                         return dragData;
48164                 }
48165         //console.log('nodragData')
48166                 return false;
48167     },
48168     
48169 /**     Specify to which ddGroup items in this DDView may be dragged. */
48170     setDraggable: function(ddGroup) {
48171         if (ddGroup instanceof Array) {
48172                 Roo.each(ddGroup, this.setDraggable, this);
48173                 return;
48174         }
48175         if (this.dragZone) {
48176                 this.dragZone.addToGroup(ddGroup);
48177         } else {
48178                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48179                                 containerScroll: true,
48180                                 ddGroup: ddGroup 
48181
48182                         });
48183 //                      Draggability implies selection. DragZone's mousedown selects the element.
48184                         if (!this.multiSelect) { this.singleSelect = true; }
48185
48186 //                      Wire the DragZone's handlers up to methods in *this*
48187                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48188                 }
48189     },
48190
48191 /**     Specify from which ddGroup this DDView accepts drops. */
48192     setDroppable: function(ddGroup) {
48193         if (ddGroup instanceof Array) {
48194                 Roo.each(ddGroup, this.setDroppable, this);
48195                 return;
48196         }
48197         if (this.dropZone) {
48198                 this.dropZone.addToGroup(ddGroup);
48199         } else {
48200                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48201                                 containerScroll: true,
48202                                 ddGroup: ddGroup
48203                         });
48204
48205 //                      Wire the DropZone's handlers up to methods in *this*
48206                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48207                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48208                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48209                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48210                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48211                 }
48212     },
48213
48214 /**     Decide whether to drop above or below a View node. */
48215     getDropPoint : function(e, n, dd){
48216         if (n == this.el.dom) { return "above"; }
48217                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48218                 var c = t + (b - t) / 2;
48219                 var y = Roo.lib.Event.getPageY(e);
48220                 if(y <= c) {
48221                         return "above";
48222                 }else{
48223                         return "below";
48224                 }
48225     },
48226
48227     onNodeEnter : function(n, dd, e, data){
48228                 return false;
48229     },
48230     
48231     onNodeOver : function(n, dd, e, data){
48232                 var pt = this.getDropPoint(e, n, dd);
48233                 // set the insert point style on the target node
48234                 var dragElClass = this.dropNotAllowed;
48235                 if (pt) {
48236                         var targetElClass;
48237                         if (pt == "above"){
48238                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48239                                 targetElClass = "x-view-drag-insert-above";
48240                         } else {
48241                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48242                                 targetElClass = "x-view-drag-insert-below";
48243                         }
48244                         if (this.lastInsertClass != targetElClass){
48245                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48246                                 this.lastInsertClass = targetElClass;
48247                         }
48248                 }
48249                 return dragElClass;
48250         },
48251
48252     onNodeOut : function(n, dd, e, data){
48253                 this.removeDropIndicators(n);
48254     },
48255
48256     onNodeDrop : function(n, dd, e, data){
48257         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48258                 return false;
48259         }
48260         var pt = this.getDropPoint(e, n, dd);
48261                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48262                 if (pt == "below") { insertAt++; }
48263                 for (var i = 0; i < data.records.length; i++) {
48264                         var r = data.records[i];
48265                         var dup = this.store.getById(r.id);
48266                         if (dup && (dd != this.dragZone)) {
48267                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48268                         } else {
48269                                 if (data.copy) {
48270                                         this.store.insert(insertAt++, r.copy());
48271                                 } else {
48272                                         data.source.isDirtyFlag = true;
48273                                         r.store.remove(r);
48274                                         this.store.insert(insertAt++, r);
48275                                 }
48276                                 this.isDirtyFlag = true;
48277                         }
48278                 }
48279                 this.dragZone.cachedTarget = null;
48280                 return true;
48281     },
48282
48283     removeDropIndicators : function(n){
48284                 if(n){
48285                         Roo.fly(n).removeClass([
48286                                 "x-view-drag-insert-above",
48287                                 "x-view-drag-insert-below"]);
48288                         this.lastInsertClass = "_noclass";
48289                 }
48290     },
48291
48292 /**
48293  *      Utility method. Add a delete option to the DDView's context menu.
48294  *      @param {String} imageUrl The URL of the "delete" icon image.
48295  */
48296         setDeletable: function(imageUrl) {
48297                 if (!this.singleSelect && !this.multiSelect) {
48298                         this.singleSelect = true;
48299                 }
48300                 var c = this.getContextMenu();
48301                 this.contextMenu.on("itemclick", function(item) {
48302                         switch (item.id) {
48303                                 case "delete":
48304                                         this.remove(this.getSelectedIndexes());
48305                                         break;
48306                         }
48307                 }, this);
48308                 this.contextMenu.add({
48309                         icon: imageUrl,
48310                         id: "delete",
48311                         text: 'Delete'
48312                 });
48313         },
48314         
48315 /**     Return the context menu for this DDView. */
48316         getContextMenu: function() {
48317                 if (!this.contextMenu) {
48318 //                      Create the View's context menu
48319                         this.contextMenu = new Roo.menu.Menu({
48320                                 id: this.id + "-contextmenu"
48321                         });
48322                         this.el.on("contextmenu", this.showContextMenu, this);
48323                 }
48324                 return this.contextMenu;
48325         },
48326         
48327         disableContextMenu: function() {
48328                 if (this.contextMenu) {
48329                         this.el.un("contextmenu", this.showContextMenu, this);
48330                 }
48331         },
48332
48333         showContextMenu: function(e, item) {
48334         item = this.findItemFromChild(e.getTarget());
48335                 if (item) {
48336                         e.stopEvent();
48337                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48338                         this.contextMenu.showAt(e.getXY());
48339             }
48340     },
48341
48342 /**
48343  *      Remove {@link Roo.data.Record}s at the specified indices.
48344  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48345  */
48346     remove: function(selectedIndices) {
48347                 selectedIndices = [].concat(selectedIndices);
48348                 for (var i = 0; i < selectedIndices.length; i++) {
48349                         var rec = this.store.getAt(selectedIndices[i]);
48350                         this.store.remove(rec);
48351                 }
48352     },
48353
48354 /**
48355  *      Double click fires the event, but also, if this is draggable, and there is only one other
48356  *      related DropZone, it transfers the selected node.
48357  */
48358     onDblClick : function(e){
48359         var item = this.findItemFromChild(e.getTarget());
48360         if(item){
48361             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48362                 return false;
48363             }
48364             if (this.dragGroup) {
48365                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48366                     while (targets.indexOf(this.dropZone) > -1) {
48367                             targets.remove(this.dropZone);
48368                                 }
48369                     if (targets.length == 1) {
48370                                         this.dragZone.cachedTarget = null;
48371                         var el = Roo.get(targets[0].getEl());
48372                         var box = el.getBox(true);
48373                         targets[0].onNodeDrop(el.dom, {
48374                                 target: el.dom,
48375                                 xy: [box.x, box.y + box.height - 1]
48376                         }, null, this.getDragData(e));
48377                     }
48378                 }
48379         }
48380     },
48381     
48382     handleSelection: function(e) {
48383                 this.dragZone.cachedTarget = null;
48384         var item = this.findItemFromChild(e.getTarget());
48385         if (!item) {
48386                 this.clearSelections(true);
48387                 return;
48388         }
48389                 if (item && (this.multiSelect || this.singleSelect)){
48390                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48391                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48392                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48393                                 this.unselect(item);
48394                         } else {
48395                                 this.select(item, this.multiSelect && e.ctrlKey);
48396                                 this.lastSelection = item;
48397                         }
48398                 }
48399     },
48400
48401     onItemClick : function(item, index, e){
48402                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48403                         return false;
48404                 }
48405                 return true;
48406     },
48407
48408     unselect : function(nodeInfo, suppressEvent){
48409                 var node = this.getNode(nodeInfo);
48410                 if(node && this.isSelected(node)){
48411                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48412                                 Roo.fly(node).removeClass(this.selectedClass);
48413                                 this.selections.remove(node);
48414                                 if(!suppressEvent){
48415                                         this.fireEvent("selectionchange", this, this.selections);
48416                                 }
48417                         }
48418                 }
48419     }
48420 });
48421 /*
48422  * Based on:
48423  * Ext JS Library 1.1.1
48424  * Copyright(c) 2006-2007, Ext JS, LLC.
48425  *
48426  * Originally Released Under LGPL - original licence link has changed is not relivant.
48427  *
48428  * Fork - LGPL
48429  * <script type="text/javascript">
48430  */
48431  
48432 /**
48433  * @class Roo.LayoutManager
48434  * @extends Roo.util.Observable
48435  * Base class for layout managers.
48436  */
48437 Roo.LayoutManager = function(container, config){
48438     Roo.LayoutManager.superclass.constructor.call(this);
48439     this.el = Roo.get(container);
48440     // ie scrollbar fix
48441     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48442         document.body.scroll = "no";
48443     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48444         this.el.position('relative');
48445     }
48446     this.id = this.el.id;
48447     this.el.addClass("x-layout-container");
48448     /** false to disable window resize monitoring @type Boolean */
48449     this.monitorWindowResize = true;
48450     this.regions = {};
48451     this.addEvents({
48452         /**
48453          * @event layout
48454          * Fires when a layout is performed. 
48455          * @param {Roo.LayoutManager} this
48456          */
48457         "layout" : true,
48458         /**
48459          * @event regionresized
48460          * Fires when the user resizes a region. 
48461          * @param {Roo.LayoutRegion} region The resized region
48462          * @param {Number} newSize The new size (width for east/west, height for north/south)
48463          */
48464         "regionresized" : true,
48465         /**
48466          * @event regioncollapsed
48467          * Fires when a region is collapsed. 
48468          * @param {Roo.LayoutRegion} region The collapsed region
48469          */
48470         "regioncollapsed" : true,
48471         /**
48472          * @event regionexpanded
48473          * Fires when a region is expanded.  
48474          * @param {Roo.LayoutRegion} region The expanded region
48475          */
48476         "regionexpanded" : true
48477     });
48478     this.updating = false;
48479     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48480 };
48481
48482 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48483     /**
48484      * Returns true if this layout is currently being updated
48485      * @return {Boolean}
48486      */
48487     isUpdating : function(){
48488         return this.updating; 
48489     },
48490     
48491     /**
48492      * Suspend the LayoutManager from doing auto-layouts while
48493      * making multiple add or remove calls
48494      */
48495     beginUpdate : function(){
48496         this.updating = true;    
48497     },
48498     
48499     /**
48500      * Restore auto-layouts and optionally disable the manager from performing a layout
48501      * @param {Boolean} noLayout true to disable a layout update 
48502      */
48503     endUpdate : function(noLayout){
48504         this.updating = false;
48505         if(!noLayout){
48506             this.layout();
48507         }    
48508     },
48509     
48510     layout: function(){
48511         
48512     },
48513     
48514     onRegionResized : function(region, newSize){
48515         this.fireEvent("regionresized", region, newSize);
48516         this.layout();
48517     },
48518     
48519     onRegionCollapsed : function(region){
48520         this.fireEvent("regioncollapsed", region);
48521     },
48522     
48523     onRegionExpanded : function(region){
48524         this.fireEvent("regionexpanded", region);
48525     },
48526         
48527     /**
48528      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48529      * performs box-model adjustments.
48530      * @return {Object} The size as an object {width: (the width), height: (the height)}
48531      */
48532     getViewSize : function(){
48533         var size;
48534         if(this.el.dom != document.body){
48535             size = this.el.getSize();
48536         }else{
48537             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48538         }
48539         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48540         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48541         return size;
48542     },
48543     
48544     /**
48545      * Returns the Element this layout is bound to.
48546      * @return {Roo.Element}
48547      */
48548     getEl : function(){
48549         return this.el;
48550     },
48551     
48552     /**
48553      * Returns the specified region.
48554      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48555      * @return {Roo.LayoutRegion}
48556      */
48557     getRegion : function(target){
48558         return this.regions[target.toLowerCase()];
48559     },
48560     
48561     onWindowResize : function(){
48562         if(this.monitorWindowResize){
48563             this.layout();
48564         }
48565     }
48566 });/*
48567  * Based on:
48568  * Ext JS Library 1.1.1
48569  * Copyright(c) 2006-2007, Ext JS, LLC.
48570  *
48571  * Originally Released Under LGPL - original licence link has changed is not relivant.
48572  *
48573  * Fork - LGPL
48574  * <script type="text/javascript">
48575  */
48576 /**
48577  * @class Roo.BorderLayout
48578  * @extends Roo.LayoutManager
48579  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48580  * please see: <br><br>
48581  * <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>
48582  * <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>
48583  * Example:
48584  <pre><code>
48585  var layout = new Roo.BorderLayout(document.body, {
48586     north: {
48587         initialSize: 25,
48588         titlebar: false
48589     },
48590     west: {
48591         split:true,
48592         initialSize: 200,
48593         minSize: 175,
48594         maxSize: 400,
48595         titlebar: true,
48596         collapsible: true
48597     },
48598     east: {
48599         split:true,
48600         initialSize: 202,
48601         minSize: 175,
48602         maxSize: 400,
48603         titlebar: true,
48604         collapsible: true
48605     },
48606     south: {
48607         split:true,
48608         initialSize: 100,
48609         minSize: 100,
48610         maxSize: 200,
48611         titlebar: true,
48612         collapsible: true
48613     },
48614     center: {
48615         titlebar: true,
48616         autoScroll:true,
48617         resizeTabs: true,
48618         minTabWidth: 50,
48619         preferredTabWidth: 150
48620     }
48621 });
48622
48623 // shorthand
48624 var CP = Roo.ContentPanel;
48625
48626 layout.beginUpdate();
48627 layout.add("north", new CP("north", "North"));
48628 layout.add("south", new CP("south", {title: "South", closable: true}));
48629 layout.add("west", new CP("west", {title: "West"}));
48630 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48631 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48632 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48633 layout.getRegion("center").showPanel("center1");
48634 layout.endUpdate();
48635 </code></pre>
48636
48637 <b>The container the layout is rendered into can be either the body element or any other element.
48638 If it is not the body element, the container needs to either be an absolute positioned element,
48639 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48640 the container size if it is not the body element.</b>
48641
48642 * @constructor
48643 * Create a new BorderLayout
48644 * @param {String/HTMLElement/Element} container The container this layout is bound to
48645 * @param {Object} config Configuration options
48646  */
48647 Roo.BorderLayout = function(container, config){
48648     config = config || {};
48649     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48650     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48651     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48652         var target = this.factory.validRegions[i];
48653         if(config[target]){
48654             this.addRegion(target, config[target]);
48655         }
48656     }
48657 };
48658
48659 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48660     /**
48661      * Creates and adds a new region if it doesn't already exist.
48662      * @param {String} target The target region key (north, south, east, west or center).
48663      * @param {Object} config The regions config object
48664      * @return {BorderLayoutRegion} The new region
48665      */
48666     addRegion : function(target, config){
48667         if(!this.regions[target]){
48668             var r = this.factory.create(target, this, config);
48669             this.bindRegion(target, r);
48670         }
48671         return this.regions[target];
48672     },
48673
48674     // private (kinda)
48675     bindRegion : function(name, r){
48676         this.regions[name] = r;
48677         r.on("visibilitychange", this.layout, this);
48678         r.on("paneladded", this.layout, this);
48679         r.on("panelremoved", this.layout, this);
48680         r.on("invalidated", this.layout, this);
48681         r.on("resized", this.onRegionResized, this);
48682         r.on("collapsed", this.onRegionCollapsed, this);
48683         r.on("expanded", this.onRegionExpanded, this);
48684     },
48685
48686     /**
48687      * Performs a layout update.
48688      */
48689     layout : function(){
48690         if(this.updating) return;
48691         var size = this.getViewSize();
48692         var w = size.width;
48693         var h = size.height;
48694         var centerW = w;
48695         var centerH = h;
48696         var centerY = 0;
48697         var centerX = 0;
48698         //var x = 0, y = 0;
48699
48700         var rs = this.regions;
48701         var north = rs["north"];
48702         var south = rs["south"]; 
48703         var west = rs["west"];
48704         var east = rs["east"];
48705         var center = rs["center"];
48706         //if(this.hideOnLayout){ // not supported anymore
48707             //c.el.setStyle("display", "none");
48708         //}
48709         if(north && north.isVisible()){
48710             var b = north.getBox();
48711             var m = north.getMargins();
48712             b.width = w - (m.left+m.right);
48713             b.x = m.left;
48714             b.y = m.top;
48715             centerY = b.height + b.y + m.bottom;
48716             centerH -= centerY;
48717             north.updateBox(this.safeBox(b));
48718         }
48719         if(south && south.isVisible()){
48720             var b = south.getBox();
48721             var m = south.getMargins();
48722             b.width = w - (m.left+m.right);
48723             b.x = m.left;
48724             var totalHeight = (b.height + m.top + m.bottom);
48725             b.y = h - totalHeight + m.top;
48726             centerH -= totalHeight;
48727             south.updateBox(this.safeBox(b));
48728         }
48729         if(west && west.isVisible()){
48730             var b = west.getBox();
48731             var m = west.getMargins();
48732             b.height = centerH - (m.top+m.bottom);
48733             b.x = m.left;
48734             b.y = centerY + m.top;
48735             var totalWidth = (b.width + m.left + m.right);
48736             centerX += totalWidth;
48737             centerW -= totalWidth;
48738             west.updateBox(this.safeBox(b));
48739         }
48740         if(east && east.isVisible()){
48741             var b = east.getBox();
48742             var m = east.getMargins();
48743             b.height = centerH - (m.top+m.bottom);
48744             var totalWidth = (b.width + m.left + m.right);
48745             b.x = w - totalWidth + m.left;
48746             b.y = centerY + m.top;
48747             centerW -= totalWidth;
48748             east.updateBox(this.safeBox(b));
48749         }
48750         if(center){
48751             var m = center.getMargins();
48752             var centerBox = {
48753                 x: centerX + m.left,
48754                 y: centerY + m.top,
48755                 width: centerW - (m.left+m.right),
48756                 height: centerH - (m.top+m.bottom)
48757             };
48758             //if(this.hideOnLayout){
48759                 //center.el.setStyle("display", "block");
48760             //}
48761             center.updateBox(this.safeBox(centerBox));
48762         }
48763         this.el.repaint();
48764         this.fireEvent("layout", this);
48765     },
48766
48767     // private
48768     safeBox : function(box){
48769         box.width = Math.max(0, box.width);
48770         box.height = Math.max(0, box.height);
48771         return box;
48772     },
48773
48774     /**
48775      * Adds a ContentPanel (or subclass) to this layout.
48776      * @param {String} target The target region key (north, south, east, west or center).
48777      * @param {Roo.ContentPanel} panel The panel to add
48778      * @return {Roo.ContentPanel} The added panel
48779      */
48780     add : function(target, panel){
48781          
48782         target = target.toLowerCase();
48783         return this.regions[target].add(panel);
48784     },
48785
48786     /**
48787      * Remove a ContentPanel (or subclass) to this layout.
48788      * @param {String} target The target region key (north, south, east, west or center).
48789      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48790      * @return {Roo.ContentPanel} The removed panel
48791      */
48792     remove : function(target, panel){
48793         target = target.toLowerCase();
48794         return this.regions[target].remove(panel);
48795     },
48796
48797     /**
48798      * Searches all regions for a panel with the specified id
48799      * @param {String} panelId
48800      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48801      */
48802     findPanel : function(panelId){
48803         var rs = this.regions;
48804         for(var target in rs){
48805             if(typeof rs[target] != "function"){
48806                 var p = rs[target].getPanel(panelId);
48807                 if(p){
48808                     return p;
48809                 }
48810             }
48811         }
48812         return null;
48813     },
48814
48815     /**
48816      * Searches all regions for a panel with the specified id and activates (shows) it.
48817      * @param {String/ContentPanel} panelId The panels id or the panel itself
48818      * @return {Roo.ContentPanel} The shown panel or null
48819      */
48820     showPanel : function(panelId) {
48821       var rs = this.regions;
48822       for(var target in rs){
48823          var r = rs[target];
48824          if(typeof r != "function"){
48825             if(r.hasPanel(panelId)){
48826                return r.showPanel(panelId);
48827             }
48828          }
48829       }
48830       return null;
48831    },
48832
48833    /**
48834      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48835      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48836      */
48837     restoreState : function(provider){
48838         if(!provider){
48839             provider = Roo.state.Manager;
48840         }
48841         var sm = new Roo.LayoutStateManager();
48842         sm.init(this, provider);
48843     },
48844
48845     /**
48846      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48847      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48848      * a valid ContentPanel config object.  Example:
48849      * <pre><code>
48850 // Create the main layout
48851 var layout = new Roo.BorderLayout('main-ct', {
48852     west: {
48853         split:true,
48854         minSize: 175,
48855         titlebar: true
48856     },
48857     center: {
48858         title:'Components'
48859     }
48860 }, 'main-ct');
48861
48862 // Create and add multiple ContentPanels at once via configs
48863 layout.batchAdd({
48864    west: {
48865        id: 'source-files',
48866        autoCreate:true,
48867        title:'Ext Source Files',
48868        autoScroll:true,
48869        fitToFrame:true
48870    },
48871    center : {
48872        el: cview,
48873        autoScroll:true,
48874        fitToFrame:true,
48875        toolbar: tb,
48876        resizeEl:'cbody'
48877    }
48878 });
48879 </code></pre>
48880      * @param {Object} regions An object containing ContentPanel configs by region name
48881      */
48882     batchAdd : function(regions){
48883         this.beginUpdate();
48884         for(var rname in regions){
48885             var lr = this.regions[rname];
48886             if(lr){
48887                 this.addTypedPanels(lr, regions[rname]);
48888             }
48889         }
48890         this.endUpdate();
48891     },
48892
48893     // private
48894     addTypedPanels : function(lr, ps){
48895         if(typeof ps == 'string'){
48896             lr.add(new Roo.ContentPanel(ps));
48897         }
48898         else if(ps instanceof Array){
48899             for(var i =0, len = ps.length; i < len; i++){
48900                 this.addTypedPanels(lr, ps[i]);
48901             }
48902         }
48903         else if(!ps.events){ // raw config?
48904             var el = ps.el;
48905             delete ps.el; // prevent conflict
48906             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48907         }
48908         else {  // panel object assumed!
48909             lr.add(ps);
48910         }
48911     },
48912     /**
48913      * Adds a xtype elements to the layout.
48914      * <pre><code>
48915
48916 layout.addxtype({
48917        xtype : 'ContentPanel',
48918        region: 'west',
48919        items: [ .... ]
48920    }
48921 );
48922
48923 layout.addxtype({
48924         xtype : 'NestedLayoutPanel',
48925         region: 'west',
48926         layout: {
48927            center: { },
48928            west: { }   
48929         },
48930         items : [ ... list of content panels or nested layout panels.. ]
48931    }
48932 );
48933 </code></pre>
48934      * @param {Object} cfg Xtype definition of item to add.
48935      */
48936     addxtype : function(cfg)
48937     {
48938         // basically accepts a pannel...
48939         // can accept a layout region..!?!?
48940         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48941         
48942         if (!cfg.xtype.match(/Panel$/)) {
48943             return false;
48944         }
48945         var ret = false;
48946         
48947         if (typeof(cfg.region) == 'undefined') {
48948             Roo.log("Failed to add Panel, region was not set");
48949             Roo.log(cfg);
48950             return false;
48951         }
48952         var region = cfg.region;
48953         delete cfg.region;
48954         
48955           
48956         var xitems = [];
48957         if (cfg.items) {
48958             xitems = cfg.items;
48959             delete cfg.items;
48960         }
48961         var nb = false;
48962         
48963         switch(cfg.xtype) 
48964         {
48965             case 'ContentPanel':  // ContentPanel (el, cfg)
48966             case 'ScrollPanel':  // ContentPanel (el, cfg)
48967             case 'ViewPanel': 
48968                 if(cfg.autoCreate) {
48969                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48970                 } else {
48971                     var el = this.el.createChild();
48972                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48973                 }
48974                 
48975                 this.add(region, ret);
48976                 break;
48977             
48978             
48979             case 'TreePanel': // our new panel!
48980                 cfg.el = this.el.createChild();
48981                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48982                 this.add(region, ret);
48983                 break;
48984             
48985             case 'NestedLayoutPanel': 
48986                 // create a new Layout (which is  a Border Layout...
48987                 var el = this.el.createChild();
48988                 var clayout = cfg.layout;
48989                 delete cfg.layout;
48990                 clayout.items   = clayout.items  || [];
48991                 // replace this exitems with the clayout ones..
48992                 xitems = clayout.items;
48993                  
48994                 
48995                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48996                     cfg.background = false;
48997                 }
48998                 var layout = new Roo.BorderLayout(el, clayout);
48999                 
49000                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49001                 //console.log('adding nested layout panel '  + cfg.toSource());
49002                 this.add(region, ret);
49003                 nb = {}; /// find first...
49004                 break;
49005                 
49006             case 'GridPanel': 
49007             
49008                 // needs grid and region
49009                 
49010                 //var el = this.getRegion(region).el.createChild();
49011                 var el = this.el.createChild();
49012                 // create the grid first...
49013                 
49014                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49015                 delete cfg.grid;
49016                 if (region == 'center' && this.active ) {
49017                     cfg.background = false;
49018                 }
49019                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49020                 
49021                 this.add(region, ret);
49022                 if (cfg.background) {
49023                     ret.on('activate', function(gp) {
49024                         if (!gp.grid.rendered) {
49025                             gp.grid.render();
49026                         }
49027                     });
49028                 } else {
49029                     grid.render();
49030                 }
49031                 break;
49032            
49033            
49034            
49035                 
49036                 
49037                 
49038             default:
49039                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49040                     
49041                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49042                     this.add(region, ret);
49043                 } else {
49044                 
49045                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49046                     return null;
49047                 }
49048                 
49049              // GridPanel (grid, cfg)
49050             
49051         }
49052         this.beginUpdate();
49053         // add children..
49054         var region = '';
49055         var abn = {};
49056         Roo.each(xitems, function(i)  {
49057             region = nb && i.region ? i.region : false;
49058             
49059             var add = ret.addxtype(i);
49060            
49061             if (region) {
49062                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49063                 if (!i.background) {
49064                     abn[region] = nb[region] ;
49065                 }
49066             }
49067             
49068         });
49069         this.endUpdate();
49070
49071         // make the last non-background panel active..
49072         //if (nb) { Roo.log(abn); }
49073         if (nb) {
49074             
49075             for(var r in abn) {
49076                 region = this.getRegion(r);
49077                 if (region) {
49078                     // tried using nb[r], but it does not work..
49079                      
49080                     region.showPanel(abn[r]);
49081                    
49082                 }
49083             }
49084         }
49085         return ret;
49086         
49087     }
49088 });
49089
49090 /**
49091  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49092  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49093  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49094  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49095  * <pre><code>
49096 // shorthand
49097 var CP = Roo.ContentPanel;
49098
49099 var layout = Roo.BorderLayout.create({
49100     north: {
49101         initialSize: 25,
49102         titlebar: false,
49103         panels: [new CP("north", "North")]
49104     },
49105     west: {
49106         split:true,
49107         initialSize: 200,
49108         minSize: 175,
49109         maxSize: 400,
49110         titlebar: true,
49111         collapsible: true,
49112         panels: [new CP("west", {title: "West"})]
49113     },
49114     east: {
49115         split:true,
49116         initialSize: 202,
49117         minSize: 175,
49118         maxSize: 400,
49119         titlebar: true,
49120         collapsible: true,
49121         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49122     },
49123     south: {
49124         split:true,
49125         initialSize: 100,
49126         minSize: 100,
49127         maxSize: 200,
49128         titlebar: true,
49129         collapsible: true,
49130         panels: [new CP("south", {title: "South", closable: true})]
49131     },
49132     center: {
49133         titlebar: true,
49134         autoScroll:true,
49135         resizeTabs: true,
49136         minTabWidth: 50,
49137         preferredTabWidth: 150,
49138         panels: [
49139             new CP("center1", {title: "Close Me", closable: true}),
49140             new CP("center2", {title: "Center Panel", closable: false})
49141         ]
49142     }
49143 }, document.body);
49144
49145 layout.getRegion("center").showPanel("center1");
49146 </code></pre>
49147  * @param config
49148  * @param targetEl
49149  */
49150 Roo.BorderLayout.create = function(config, targetEl){
49151     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49152     layout.beginUpdate();
49153     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49154     for(var j = 0, jlen = regions.length; j < jlen; j++){
49155         var lr = regions[j];
49156         if(layout.regions[lr] && config[lr].panels){
49157             var r = layout.regions[lr];
49158             var ps = config[lr].panels;
49159             layout.addTypedPanels(r, ps);
49160         }
49161     }
49162     layout.endUpdate();
49163     return layout;
49164 };
49165
49166 // private
49167 Roo.BorderLayout.RegionFactory = {
49168     // private
49169     validRegions : ["north","south","east","west","center"],
49170
49171     // private
49172     create : function(target, mgr, config){
49173         target = target.toLowerCase();
49174         if(config.lightweight || config.basic){
49175             return new Roo.BasicLayoutRegion(mgr, config, target);
49176         }
49177         switch(target){
49178             case "north":
49179                 return new Roo.NorthLayoutRegion(mgr, config);
49180             case "south":
49181                 return new Roo.SouthLayoutRegion(mgr, config);
49182             case "east":
49183                 return new Roo.EastLayoutRegion(mgr, config);
49184             case "west":
49185                 return new Roo.WestLayoutRegion(mgr, config);
49186             case "center":
49187                 return new Roo.CenterLayoutRegion(mgr, config);
49188         }
49189         throw 'Layout region "'+target+'" not supported.';
49190     }
49191 };/*
49192  * Based on:
49193  * Ext JS Library 1.1.1
49194  * Copyright(c) 2006-2007, Ext JS, LLC.
49195  *
49196  * Originally Released Under LGPL - original licence link has changed is not relivant.
49197  *
49198  * Fork - LGPL
49199  * <script type="text/javascript">
49200  */
49201  
49202 /**
49203  * @class Roo.BasicLayoutRegion
49204  * @extends Roo.util.Observable
49205  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49206  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49207  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49208  */
49209 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49210     this.mgr = mgr;
49211     this.position  = pos;
49212     this.events = {
49213         /**
49214          * @scope Roo.BasicLayoutRegion
49215          */
49216         
49217         /**
49218          * @event beforeremove
49219          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49220          * @param {Roo.LayoutRegion} this
49221          * @param {Roo.ContentPanel} panel The panel
49222          * @param {Object} e The cancel event object
49223          */
49224         "beforeremove" : true,
49225         /**
49226          * @event invalidated
49227          * Fires when the layout for this region is changed.
49228          * @param {Roo.LayoutRegion} this
49229          */
49230         "invalidated" : true,
49231         /**
49232          * @event visibilitychange
49233          * Fires when this region is shown or hidden 
49234          * @param {Roo.LayoutRegion} this
49235          * @param {Boolean} visibility true or false
49236          */
49237         "visibilitychange" : true,
49238         /**
49239          * @event paneladded
49240          * Fires when a panel is added. 
49241          * @param {Roo.LayoutRegion} this
49242          * @param {Roo.ContentPanel} panel The panel
49243          */
49244         "paneladded" : true,
49245         /**
49246          * @event panelremoved
49247          * Fires when a panel is removed. 
49248          * @param {Roo.LayoutRegion} this
49249          * @param {Roo.ContentPanel} panel The panel
49250          */
49251         "panelremoved" : true,
49252         /**
49253          * @event collapsed
49254          * Fires when this region is collapsed.
49255          * @param {Roo.LayoutRegion} this
49256          */
49257         "collapsed" : true,
49258         /**
49259          * @event expanded
49260          * Fires when this region is expanded.
49261          * @param {Roo.LayoutRegion} this
49262          */
49263         "expanded" : true,
49264         /**
49265          * @event slideshow
49266          * Fires when this region is slid into view.
49267          * @param {Roo.LayoutRegion} this
49268          */
49269         "slideshow" : true,
49270         /**
49271          * @event slidehide
49272          * Fires when this region slides out of view. 
49273          * @param {Roo.LayoutRegion} this
49274          */
49275         "slidehide" : true,
49276         /**
49277          * @event panelactivated
49278          * Fires when a panel is activated. 
49279          * @param {Roo.LayoutRegion} this
49280          * @param {Roo.ContentPanel} panel The activated panel
49281          */
49282         "panelactivated" : true,
49283         /**
49284          * @event resized
49285          * Fires when the user resizes this region. 
49286          * @param {Roo.LayoutRegion} this
49287          * @param {Number} newSize The new size (width for east/west, height for north/south)
49288          */
49289         "resized" : true
49290     };
49291     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49292     this.panels = new Roo.util.MixedCollection();
49293     this.panels.getKey = this.getPanelId.createDelegate(this);
49294     this.box = null;
49295     this.activePanel = null;
49296     // ensure listeners are added...
49297     
49298     if (config.listeners || config.events) {
49299         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49300             listeners : config.listeners || {},
49301             events : config.events || {}
49302         });
49303     }
49304     
49305     if(skipConfig !== true){
49306         this.applyConfig(config);
49307     }
49308 };
49309
49310 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49311     getPanelId : function(p){
49312         return p.getId();
49313     },
49314     
49315     applyConfig : function(config){
49316         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49317         this.config = config;
49318         
49319     },
49320     
49321     /**
49322      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49323      * the width, for horizontal (north, south) the height.
49324      * @param {Number} newSize The new width or height
49325      */
49326     resizeTo : function(newSize){
49327         var el = this.el ? this.el :
49328                  (this.activePanel ? this.activePanel.getEl() : null);
49329         if(el){
49330             switch(this.position){
49331                 case "east":
49332                 case "west":
49333                     el.setWidth(newSize);
49334                     this.fireEvent("resized", this, newSize);
49335                 break;
49336                 case "north":
49337                 case "south":
49338                     el.setHeight(newSize);
49339                     this.fireEvent("resized", this, newSize);
49340                 break;                
49341             }
49342         }
49343     },
49344     
49345     getBox : function(){
49346         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49347     },
49348     
49349     getMargins : function(){
49350         return this.margins;
49351     },
49352     
49353     updateBox : function(box){
49354         this.box = box;
49355         var el = this.activePanel.getEl();
49356         el.dom.style.left = box.x + "px";
49357         el.dom.style.top = box.y + "px";
49358         this.activePanel.setSize(box.width, box.height);
49359     },
49360     
49361     /**
49362      * Returns the container element for this region.
49363      * @return {Roo.Element}
49364      */
49365     getEl : function(){
49366         return this.activePanel;
49367     },
49368     
49369     /**
49370      * Returns true if this region is currently visible.
49371      * @return {Boolean}
49372      */
49373     isVisible : function(){
49374         return this.activePanel ? true : false;
49375     },
49376     
49377     setActivePanel : function(panel){
49378         panel = this.getPanel(panel);
49379         if(this.activePanel && this.activePanel != panel){
49380             this.activePanel.setActiveState(false);
49381             this.activePanel.getEl().setLeftTop(-10000,-10000);
49382         }
49383         this.activePanel = panel;
49384         panel.setActiveState(true);
49385         if(this.box){
49386             panel.setSize(this.box.width, this.box.height);
49387         }
49388         this.fireEvent("panelactivated", this, panel);
49389         this.fireEvent("invalidated");
49390     },
49391     
49392     /**
49393      * Show the specified panel.
49394      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49395      * @return {Roo.ContentPanel} The shown panel or null
49396      */
49397     showPanel : function(panel){
49398         if(panel = this.getPanel(panel)){
49399             this.setActivePanel(panel);
49400         }
49401         return panel;
49402     },
49403     
49404     /**
49405      * Get the active panel for this region.
49406      * @return {Roo.ContentPanel} The active panel or null
49407      */
49408     getActivePanel : function(){
49409         return this.activePanel;
49410     },
49411     
49412     /**
49413      * Add the passed ContentPanel(s)
49414      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49415      * @return {Roo.ContentPanel} The panel added (if only one was added)
49416      */
49417     add : function(panel){
49418         if(arguments.length > 1){
49419             for(var i = 0, len = arguments.length; i < len; i++) {
49420                 this.add(arguments[i]);
49421             }
49422             return null;
49423         }
49424         if(this.hasPanel(panel)){
49425             this.showPanel(panel);
49426             return panel;
49427         }
49428         var el = panel.getEl();
49429         if(el.dom.parentNode != this.mgr.el.dom){
49430             this.mgr.el.dom.appendChild(el.dom);
49431         }
49432         if(panel.setRegion){
49433             panel.setRegion(this);
49434         }
49435         this.panels.add(panel);
49436         el.setStyle("position", "absolute");
49437         if(!panel.background){
49438             this.setActivePanel(panel);
49439             if(this.config.initialSize && this.panels.getCount()==1){
49440                 this.resizeTo(this.config.initialSize);
49441             }
49442         }
49443         this.fireEvent("paneladded", this, panel);
49444         return panel;
49445     },
49446     
49447     /**
49448      * Returns true if the panel is in this region.
49449      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49450      * @return {Boolean}
49451      */
49452     hasPanel : function(panel){
49453         if(typeof panel == "object"){ // must be panel obj
49454             panel = panel.getId();
49455         }
49456         return this.getPanel(panel) ? true : false;
49457     },
49458     
49459     /**
49460      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49461      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49462      * @param {Boolean} preservePanel Overrides the config preservePanel option
49463      * @return {Roo.ContentPanel} The panel that was removed
49464      */
49465     remove : function(panel, preservePanel){
49466         panel = this.getPanel(panel);
49467         if(!panel){
49468             return null;
49469         }
49470         var e = {};
49471         this.fireEvent("beforeremove", this, panel, e);
49472         if(e.cancel === true){
49473             return null;
49474         }
49475         var panelId = panel.getId();
49476         this.panels.removeKey(panelId);
49477         return panel;
49478     },
49479     
49480     /**
49481      * Returns the panel specified or null if it's not in this region.
49482      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49483      * @return {Roo.ContentPanel}
49484      */
49485     getPanel : function(id){
49486         if(typeof id == "object"){ // must be panel obj
49487             return id;
49488         }
49489         return this.panels.get(id);
49490     },
49491     
49492     /**
49493      * Returns this regions position (north/south/east/west/center).
49494      * @return {String} 
49495      */
49496     getPosition: function(){
49497         return this.position;    
49498     }
49499 });/*
49500  * Based on:
49501  * Ext JS Library 1.1.1
49502  * Copyright(c) 2006-2007, Ext JS, LLC.
49503  *
49504  * Originally Released Under LGPL - original licence link has changed is not relivant.
49505  *
49506  * Fork - LGPL
49507  * <script type="text/javascript">
49508  */
49509  
49510 /**
49511  * @class Roo.LayoutRegion
49512  * @extends Roo.BasicLayoutRegion
49513  * This class represents a region in a layout manager.
49514  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49515  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49516  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49517  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49518  * @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})
49519  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49520  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49521  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49522  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49523  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49524  * @cfg {String}    title           The title for the region (overrides panel titles)
49525  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49526  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49527  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49528  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49529  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49530  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49531  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49532  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49533  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49534  * @cfg {Boolean}   showPin         True to show a pin button
49535  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49536  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49537  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49538  * @cfg {Number}    width           For East/West panels
49539  * @cfg {Number}    height          For North/South panels
49540  * @cfg {Boolean}   split           To show the splitter
49541  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49542  */
49543 Roo.LayoutRegion = function(mgr, config, pos){
49544     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49545     var dh = Roo.DomHelper;
49546     /** This region's container element 
49547     * @type Roo.Element */
49548     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49549     /** This region's title element 
49550     * @type Roo.Element */
49551
49552     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49553         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49554         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49555     ]}, true);
49556     this.titleEl.enableDisplayMode();
49557     /** This region's title text element 
49558     * @type HTMLElement */
49559     this.titleTextEl = this.titleEl.dom.firstChild;
49560     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49561     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49562     this.closeBtn.enableDisplayMode();
49563     this.closeBtn.on("click", this.closeClicked, this);
49564     this.closeBtn.hide();
49565
49566     this.createBody(config);
49567     this.visible = true;
49568     this.collapsed = false;
49569
49570     if(config.hideWhenEmpty){
49571         this.hide();
49572         this.on("paneladded", this.validateVisibility, this);
49573         this.on("panelremoved", this.validateVisibility, this);
49574     }
49575     this.applyConfig(config);
49576 };
49577
49578 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49579
49580     createBody : function(){
49581         /** This region's body element 
49582         * @type Roo.Element */
49583         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49584     },
49585
49586     applyConfig : function(c){
49587         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49588             var dh = Roo.DomHelper;
49589             if(c.titlebar !== false){
49590                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49591                 this.collapseBtn.on("click", this.collapse, this);
49592                 this.collapseBtn.enableDisplayMode();
49593
49594                 if(c.showPin === true || this.showPin){
49595                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49596                     this.stickBtn.enableDisplayMode();
49597                     this.stickBtn.on("click", this.expand, this);
49598                     this.stickBtn.hide();
49599                 }
49600             }
49601             /** This region's collapsed element
49602             * @type Roo.Element */
49603             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49604                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49605             ]}, true);
49606             if(c.floatable !== false){
49607                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49608                this.collapsedEl.on("click", this.collapseClick, this);
49609             }
49610
49611             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49612                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49613                    id: "message", unselectable: "on", style:{"float":"left"}});
49614                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49615              }
49616             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49617             this.expandBtn.on("click", this.expand, this);
49618         }
49619         if(this.collapseBtn){
49620             this.collapseBtn.setVisible(c.collapsible == true);
49621         }
49622         this.cmargins = c.cmargins || this.cmargins ||
49623                          (this.position == "west" || this.position == "east" ?
49624                              {top: 0, left: 2, right:2, bottom: 0} :
49625                              {top: 2, left: 0, right:0, bottom: 2});
49626         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49627         this.bottomTabs = c.tabPosition != "top";
49628         this.autoScroll = c.autoScroll || false;
49629         if(this.autoScroll){
49630             this.bodyEl.setStyle("overflow", "auto");
49631         }else{
49632             this.bodyEl.setStyle("overflow", "hidden");
49633         }
49634         //if(c.titlebar !== false){
49635             if((!c.titlebar && !c.title) || c.titlebar === false){
49636                 this.titleEl.hide();
49637             }else{
49638                 this.titleEl.show();
49639                 if(c.title){
49640                     this.titleTextEl.innerHTML = c.title;
49641                 }
49642             }
49643         //}
49644         this.duration = c.duration || .30;
49645         this.slideDuration = c.slideDuration || .45;
49646         this.config = c;
49647         if(c.collapsed){
49648             this.collapse(true);
49649         }
49650         if(c.hidden){
49651             this.hide();
49652         }
49653     },
49654     /**
49655      * Returns true if this region is currently visible.
49656      * @return {Boolean}
49657      */
49658     isVisible : function(){
49659         return this.visible;
49660     },
49661
49662     /**
49663      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49664      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49665      */
49666     setCollapsedTitle : function(title){
49667         title = title || "&#160;";
49668         if(this.collapsedTitleTextEl){
49669             this.collapsedTitleTextEl.innerHTML = title;
49670         }
49671     },
49672
49673     getBox : function(){
49674         var b;
49675         if(!this.collapsed){
49676             b = this.el.getBox(false, true);
49677         }else{
49678             b = this.collapsedEl.getBox(false, true);
49679         }
49680         return b;
49681     },
49682
49683     getMargins : function(){
49684         return this.collapsed ? this.cmargins : this.margins;
49685     },
49686
49687     highlight : function(){
49688         this.el.addClass("x-layout-panel-dragover");
49689     },
49690
49691     unhighlight : function(){
49692         this.el.removeClass("x-layout-panel-dragover");
49693     },
49694
49695     updateBox : function(box){
49696         this.box = box;
49697         if(!this.collapsed){
49698             this.el.dom.style.left = box.x + "px";
49699             this.el.dom.style.top = box.y + "px";
49700             this.updateBody(box.width, box.height);
49701         }else{
49702             this.collapsedEl.dom.style.left = box.x + "px";
49703             this.collapsedEl.dom.style.top = box.y + "px";
49704             this.collapsedEl.setSize(box.width, box.height);
49705         }
49706         if(this.tabs){
49707             this.tabs.autoSizeTabs();
49708         }
49709     },
49710
49711     updateBody : function(w, h){
49712         if(w !== null){
49713             this.el.setWidth(w);
49714             w -= this.el.getBorderWidth("rl");
49715             if(this.config.adjustments){
49716                 w += this.config.adjustments[0];
49717             }
49718         }
49719         if(h !== null){
49720             this.el.setHeight(h);
49721             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49722             h -= this.el.getBorderWidth("tb");
49723             if(this.config.adjustments){
49724                 h += this.config.adjustments[1];
49725             }
49726             this.bodyEl.setHeight(h);
49727             if(this.tabs){
49728                 h = this.tabs.syncHeight(h);
49729             }
49730         }
49731         if(this.panelSize){
49732             w = w !== null ? w : this.panelSize.width;
49733             h = h !== null ? h : this.panelSize.height;
49734         }
49735         if(this.activePanel){
49736             var el = this.activePanel.getEl();
49737             w = w !== null ? w : el.getWidth();
49738             h = h !== null ? h : el.getHeight();
49739             this.panelSize = {width: w, height: h};
49740             this.activePanel.setSize(w, h);
49741         }
49742         if(Roo.isIE && this.tabs){
49743             this.tabs.el.repaint();
49744         }
49745     },
49746
49747     /**
49748      * Returns the container element for this region.
49749      * @return {Roo.Element}
49750      */
49751     getEl : function(){
49752         return this.el;
49753     },
49754
49755     /**
49756      * Hides this region.
49757      */
49758     hide : function(){
49759         if(!this.collapsed){
49760             this.el.dom.style.left = "-2000px";
49761             this.el.hide();
49762         }else{
49763             this.collapsedEl.dom.style.left = "-2000px";
49764             this.collapsedEl.hide();
49765         }
49766         this.visible = false;
49767         this.fireEvent("visibilitychange", this, false);
49768     },
49769
49770     /**
49771      * Shows this region if it was previously hidden.
49772      */
49773     show : function(){
49774         if(!this.collapsed){
49775             this.el.show();
49776         }else{
49777             this.collapsedEl.show();
49778         }
49779         this.visible = true;
49780         this.fireEvent("visibilitychange", this, true);
49781     },
49782
49783     closeClicked : function(){
49784         if(this.activePanel){
49785             this.remove(this.activePanel);
49786         }
49787     },
49788
49789     collapseClick : function(e){
49790         if(this.isSlid){
49791            e.stopPropagation();
49792            this.slideIn();
49793         }else{
49794            e.stopPropagation();
49795            this.slideOut();
49796         }
49797     },
49798
49799     /**
49800      * Collapses this region.
49801      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49802      */
49803     collapse : function(skipAnim){
49804         if(this.collapsed) return;
49805         this.collapsed = true;
49806         if(this.split){
49807             this.split.el.hide();
49808         }
49809         if(this.config.animate && skipAnim !== true){
49810             this.fireEvent("invalidated", this);
49811             this.animateCollapse();
49812         }else{
49813             this.el.setLocation(-20000,-20000);
49814             this.el.hide();
49815             this.collapsedEl.show();
49816             this.fireEvent("collapsed", this);
49817             this.fireEvent("invalidated", this);
49818         }
49819     },
49820
49821     animateCollapse : function(){
49822         // overridden
49823     },
49824
49825     /**
49826      * Expands this region if it was previously collapsed.
49827      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49828      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49829      */
49830     expand : function(e, skipAnim){
49831         if(e) e.stopPropagation();
49832         if(!this.collapsed || this.el.hasActiveFx()) return;
49833         if(this.isSlid){
49834             this.afterSlideIn();
49835             skipAnim = true;
49836         }
49837         this.collapsed = false;
49838         if(this.config.animate && skipAnim !== true){
49839             this.animateExpand();
49840         }else{
49841             this.el.show();
49842             if(this.split){
49843                 this.split.el.show();
49844             }
49845             this.collapsedEl.setLocation(-2000,-2000);
49846             this.collapsedEl.hide();
49847             this.fireEvent("invalidated", this);
49848             this.fireEvent("expanded", this);
49849         }
49850     },
49851
49852     animateExpand : function(){
49853         // overridden
49854     },
49855
49856     initTabs : function()
49857     {
49858         this.bodyEl.setStyle("overflow", "hidden");
49859         var ts = new Roo.TabPanel(
49860                 this.bodyEl.dom,
49861                 {
49862                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49863                     disableTooltips: this.config.disableTabTips,
49864                     toolbar : this.config.toolbar
49865                 }
49866         );
49867         if(this.config.hideTabs){
49868             ts.stripWrap.setDisplayed(false);
49869         }
49870         this.tabs = ts;
49871         ts.resizeTabs = this.config.resizeTabs === true;
49872         ts.minTabWidth = this.config.minTabWidth || 40;
49873         ts.maxTabWidth = this.config.maxTabWidth || 250;
49874         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49875         ts.monitorResize = false;
49876         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49877         ts.bodyEl.addClass('x-layout-tabs-body');
49878         this.panels.each(this.initPanelAsTab, this);
49879     },
49880
49881     initPanelAsTab : function(panel){
49882         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49883                     this.config.closeOnTab && panel.isClosable());
49884         if(panel.tabTip !== undefined){
49885             ti.setTooltip(panel.tabTip);
49886         }
49887         ti.on("activate", function(){
49888               this.setActivePanel(panel);
49889         }, this);
49890         if(this.config.closeOnTab){
49891             ti.on("beforeclose", function(t, e){
49892                 e.cancel = true;
49893                 this.remove(panel);
49894             }, this);
49895         }
49896         return ti;
49897     },
49898
49899     updatePanelTitle : function(panel, title){
49900         if(this.activePanel == panel){
49901             this.updateTitle(title);
49902         }
49903         if(this.tabs){
49904             var ti = this.tabs.getTab(panel.getEl().id);
49905             ti.setText(title);
49906             if(panel.tabTip !== undefined){
49907                 ti.setTooltip(panel.tabTip);
49908             }
49909         }
49910     },
49911
49912     updateTitle : function(title){
49913         if(this.titleTextEl && !this.config.title){
49914             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49915         }
49916     },
49917
49918     setActivePanel : function(panel){
49919         panel = this.getPanel(panel);
49920         if(this.activePanel && this.activePanel != panel){
49921             this.activePanel.setActiveState(false);
49922         }
49923         this.activePanel = panel;
49924         panel.setActiveState(true);
49925         if(this.panelSize){
49926             panel.setSize(this.panelSize.width, this.panelSize.height);
49927         }
49928         if(this.closeBtn){
49929             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49930         }
49931         this.updateTitle(panel.getTitle());
49932         if(this.tabs){
49933             this.fireEvent("invalidated", this);
49934         }
49935         this.fireEvent("panelactivated", this, panel);
49936     },
49937
49938     /**
49939      * Shows the specified panel.
49940      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49941      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49942      */
49943     showPanel : function(panel){
49944         if(panel = this.getPanel(panel)){
49945             if(this.tabs){
49946                 var tab = this.tabs.getTab(panel.getEl().id);
49947                 if(tab.isHidden()){
49948                     this.tabs.unhideTab(tab.id);
49949                 }
49950                 tab.activate();
49951             }else{
49952                 this.setActivePanel(panel);
49953             }
49954         }
49955         return panel;
49956     },
49957
49958     /**
49959      * Get the active panel for this region.
49960      * @return {Roo.ContentPanel} The active panel or null
49961      */
49962     getActivePanel : function(){
49963         return this.activePanel;
49964     },
49965
49966     validateVisibility : function(){
49967         if(this.panels.getCount() < 1){
49968             this.updateTitle("&#160;");
49969             this.closeBtn.hide();
49970             this.hide();
49971         }else{
49972             if(!this.isVisible()){
49973                 this.show();
49974             }
49975         }
49976     },
49977
49978     /**
49979      * Adds the passed ContentPanel(s) to this region.
49980      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49981      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49982      */
49983     add : function(panel){
49984         if(arguments.length > 1){
49985             for(var i = 0, len = arguments.length; i < len; i++) {
49986                 this.add(arguments[i]);
49987             }
49988             return null;
49989         }
49990         if(this.hasPanel(panel)){
49991             this.showPanel(panel);
49992             return panel;
49993         }
49994         panel.setRegion(this);
49995         this.panels.add(panel);
49996         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49997             this.bodyEl.dom.appendChild(panel.getEl().dom);
49998             if(panel.background !== true){
49999                 this.setActivePanel(panel);
50000             }
50001             this.fireEvent("paneladded", this, panel);
50002             return panel;
50003         }
50004         if(!this.tabs){
50005             this.initTabs();
50006         }else{
50007             this.initPanelAsTab(panel);
50008         }
50009         if(panel.background !== true){
50010             this.tabs.activate(panel.getEl().id);
50011         }
50012         this.fireEvent("paneladded", this, panel);
50013         return panel;
50014     },
50015
50016     /**
50017      * Hides the tab for the specified panel.
50018      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50019      */
50020     hidePanel : function(panel){
50021         if(this.tabs && (panel = this.getPanel(panel))){
50022             this.tabs.hideTab(panel.getEl().id);
50023         }
50024     },
50025
50026     /**
50027      * Unhides the tab for a previously hidden panel.
50028      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50029      */
50030     unhidePanel : function(panel){
50031         if(this.tabs && (panel = this.getPanel(panel))){
50032             this.tabs.unhideTab(panel.getEl().id);
50033         }
50034     },
50035
50036     clearPanels : function(){
50037         while(this.panels.getCount() > 0){
50038              this.remove(this.panels.first());
50039         }
50040     },
50041
50042     /**
50043      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50044      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50045      * @param {Boolean} preservePanel Overrides the config preservePanel option
50046      * @return {Roo.ContentPanel} The panel that was removed
50047      */
50048     remove : function(panel, preservePanel){
50049         panel = this.getPanel(panel);
50050         if(!panel){
50051             return null;
50052         }
50053         var e = {};
50054         this.fireEvent("beforeremove", this, panel, e);
50055         if(e.cancel === true){
50056             return null;
50057         }
50058         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50059         var panelId = panel.getId();
50060         this.panels.removeKey(panelId);
50061         if(preservePanel){
50062             document.body.appendChild(panel.getEl().dom);
50063         }
50064         if(this.tabs){
50065             this.tabs.removeTab(panel.getEl().id);
50066         }else if (!preservePanel){
50067             this.bodyEl.dom.removeChild(panel.getEl().dom);
50068         }
50069         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50070             var p = this.panels.first();
50071             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50072             tempEl.appendChild(p.getEl().dom);
50073             this.bodyEl.update("");
50074             this.bodyEl.dom.appendChild(p.getEl().dom);
50075             tempEl = null;
50076             this.updateTitle(p.getTitle());
50077             this.tabs = null;
50078             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50079             this.setActivePanel(p);
50080         }
50081         panel.setRegion(null);
50082         if(this.activePanel == panel){
50083             this.activePanel = null;
50084         }
50085         if(this.config.autoDestroy !== false && preservePanel !== true){
50086             try{panel.destroy();}catch(e){}
50087         }
50088         this.fireEvent("panelremoved", this, panel);
50089         return panel;
50090     },
50091
50092     /**
50093      * Returns the TabPanel component used by this region
50094      * @return {Roo.TabPanel}
50095      */
50096     getTabs : function(){
50097         return this.tabs;
50098     },
50099
50100     createTool : function(parentEl, className){
50101         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50102             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50103         btn.addClassOnOver("x-layout-tools-button-over");
50104         return btn;
50105     }
50106 });/*
50107  * Based on:
50108  * Ext JS Library 1.1.1
50109  * Copyright(c) 2006-2007, Ext JS, LLC.
50110  *
50111  * Originally Released Under LGPL - original licence link has changed is not relivant.
50112  *
50113  * Fork - LGPL
50114  * <script type="text/javascript">
50115  */
50116  
50117
50118
50119 /**
50120  * @class Roo.SplitLayoutRegion
50121  * @extends Roo.LayoutRegion
50122  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50123  */
50124 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50125     this.cursor = cursor;
50126     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50127 };
50128
50129 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50130     splitTip : "Drag to resize.",
50131     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50132     useSplitTips : false,
50133
50134     applyConfig : function(config){
50135         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50136         if(config.split){
50137             if(!this.split){
50138                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50139                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50140                 /** The SplitBar for this region 
50141                 * @type Roo.SplitBar */
50142                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50143                 this.split.on("moved", this.onSplitMove, this);
50144                 this.split.useShim = config.useShim === true;
50145                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50146                 if(this.useSplitTips){
50147                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50148                 }
50149                 if(config.collapsible){
50150                     this.split.el.on("dblclick", this.collapse,  this);
50151                 }
50152             }
50153             if(typeof config.minSize != "undefined"){
50154                 this.split.minSize = config.minSize;
50155             }
50156             if(typeof config.maxSize != "undefined"){
50157                 this.split.maxSize = config.maxSize;
50158             }
50159             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50160                 this.hideSplitter();
50161             }
50162         }
50163     },
50164
50165     getHMaxSize : function(){
50166          var cmax = this.config.maxSize || 10000;
50167          var center = this.mgr.getRegion("center");
50168          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50169     },
50170
50171     getVMaxSize : function(){
50172          var cmax = this.config.maxSize || 10000;
50173          var center = this.mgr.getRegion("center");
50174          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50175     },
50176
50177     onSplitMove : function(split, newSize){
50178         this.fireEvent("resized", this, newSize);
50179     },
50180     
50181     /** 
50182      * Returns the {@link Roo.SplitBar} for this region.
50183      * @return {Roo.SplitBar}
50184      */
50185     getSplitBar : function(){
50186         return this.split;
50187     },
50188     
50189     hide : function(){
50190         this.hideSplitter();
50191         Roo.SplitLayoutRegion.superclass.hide.call(this);
50192     },
50193
50194     hideSplitter : function(){
50195         if(this.split){
50196             this.split.el.setLocation(-2000,-2000);
50197             this.split.el.hide();
50198         }
50199     },
50200
50201     show : function(){
50202         if(this.split){
50203             this.split.el.show();
50204         }
50205         Roo.SplitLayoutRegion.superclass.show.call(this);
50206     },
50207     
50208     beforeSlide: function(){
50209         if(Roo.isGecko){// firefox overflow auto bug workaround
50210             this.bodyEl.clip();
50211             if(this.tabs) this.tabs.bodyEl.clip();
50212             if(this.activePanel){
50213                 this.activePanel.getEl().clip();
50214                 
50215                 if(this.activePanel.beforeSlide){
50216                     this.activePanel.beforeSlide();
50217                 }
50218             }
50219         }
50220     },
50221     
50222     afterSlide : function(){
50223         if(Roo.isGecko){// firefox overflow auto bug workaround
50224             this.bodyEl.unclip();
50225             if(this.tabs) this.tabs.bodyEl.unclip();
50226             if(this.activePanel){
50227                 this.activePanel.getEl().unclip();
50228                 if(this.activePanel.afterSlide){
50229                     this.activePanel.afterSlide();
50230                 }
50231             }
50232         }
50233     },
50234
50235     initAutoHide : function(){
50236         if(this.autoHide !== false){
50237             if(!this.autoHideHd){
50238                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50239                 this.autoHideHd = {
50240                     "mouseout": function(e){
50241                         if(!e.within(this.el, true)){
50242                             st.delay(500);
50243                         }
50244                     },
50245                     "mouseover" : function(e){
50246                         st.cancel();
50247                     },
50248                     scope : this
50249                 };
50250             }
50251             this.el.on(this.autoHideHd);
50252         }
50253     },
50254
50255     clearAutoHide : function(){
50256         if(this.autoHide !== false){
50257             this.el.un("mouseout", this.autoHideHd.mouseout);
50258             this.el.un("mouseover", this.autoHideHd.mouseover);
50259         }
50260     },
50261
50262     clearMonitor : function(){
50263         Roo.get(document).un("click", this.slideInIf, this);
50264     },
50265
50266     // these names are backwards but not changed for compat
50267     slideOut : function(){
50268         if(this.isSlid || this.el.hasActiveFx()){
50269             return;
50270         }
50271         this.isSlid = true;
50272         if(this.collapseBtn){
50273             this.collapseBtn.hide();
50274         }
50275         this.closeBtnState = this.closeBtn.getStyle('display');
50276         this.closeBtn.hide();
50277         if(this.stickBtn){
50278             this.stickBtn.show();
50279         }
50280         this.el.show();
50281         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50282         this.beforeSlide();
50283         this.el.setStyle("z-index", 10001);
50284         this.el.slideIn(this.getSlideAnchor(), {
50285             callback: function(){
50286                 this.afterSlide();
50287                 this.initAutoHide();
50288                 Roo.get(document).on("click", this.slideInIf, this);
50289                 this.fireEvent("slideshow", this);
50290             },
50291             scope: this,
50292             block: true
50293         });
50294     },
50295
50296     afterSlideIn : function(){
50297         this.clearAutoHide();
50298         this.isSlid = false;
50299         this.clearMonitor();
50300         this.el.setStyle("z-index", "");
50301         if(this.collapseBtn){
50302             this.collapseBtn.show();
50303         }
50304         this.closeBtn.setStyle('display', this.closeBtnState);
50305         if(this.stickBtn){
50306             this.stickBtn.hide();
50307         }
50308         this.fireEvent("slidehide", this);
50309     },
50310
50311     slideIn : function(cb){
50312         if(!this.isSlid || this.el.hasActiveFx()){
50313             Roo.callback(cb);
50314             return;
50315         }
50316         this.isSlid = false;
50317         this.beforeSlide();
50318         this.el.slideOut(this.getSlideAnchor(), {
50319             callback: function(){
50320                 this.el.setLeftTop(-10000, -10000);
50321                 this.afterSlide();
50322                 this.afterSlideIn();
50323                 Roo.callback(cb);
50324             },
50325             scope: this,
50326             block: true
50327         });
50328     },
50329     
50330     slideInIf : function(e){
50331         if(!e.within(this.el)){
50332             this.slideIn();
50333         }
50334     },
50335
50336     animateCollapse : function(){
50337         this.beforeSlide();
50338         this.el.setStyle("z-index", 20000);
50339         var anchor = this.getSlideAnchor();
50340         this.el.slideOut(anchor, {
50341             callback : function(){
50342                 this.el.setStyle("z-index", "");
50343                 this.collapsedEl.slideIn(anchor, {duration:.3});
50344                 this.afterSlide();
50345                 this.el.setLocation(-10000,-10000);
50346                 this.el.hide();
50347                 this.fireEvent("collapsed", this);
50348             },
50349             scope: this,
50350             block: true
50351         });
50352     },
50353
50354     animateExpand : function(){
50355         this.beforeSlide();
50356         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50357         this.el.setStyle("z-index", 20000);
50358         this.collapsedEl.hide({
50359             duration:.1
50360         });
50361         this.el.slideIn(this.getSlideAnchor(), {
50362             callback : function(){
50363                 this.el.setStyle("z-index", "");
50364                 this.afterSlide();
50365                 if(this.split){
50366                     this.split.el.show();
50367                 }
50368                 this.fireEvent("invalidated", this);
50369                 this.fireEvent("expanded", this);
50370             },
50371             scope: this,
50372             block: true
50373         });
50374     },
50375
50376     anchors : {
50377         "west" : "left",
50378         "east" : "right",
50379         "north" : "top",
50380         "south" : "bottom"
50381     },
50382
50383     sanchors : {
50384         "west" : "l",
50385         "east" : "r",
50386         "north" : "t",
50387         "south" : "b"
50388     },
50389
50390     canchors : {
50391         "west" : "tl-tr",
50392         "east" : "tr-tl",
50393         "north" : "tl-bl",
50394         "south" : "bl-tl"
50395     },
50396
50397     getAnchor : function(){
50398         return this.anchors[this.position];
50399     },
50400
50401     getCollapseAnchor : function(){
50402         return this.canchors[this.position];
50403     },
50404
50405     getSlideAnchor : function(){
50406         return this.sanchors[this.position];
50407     },
50408
50409     getAlignAdj : function(){
50410         var cm = this.cmargins;
50411         switch(this.position){
50412             case "west":
50413                 return [0, 0];
50414             break;
50415             case "east":
50416                 return [0, 0];
50417             break;
50418             case "north":
50419                 return [0, 0];
50420             break;
50421             case "south":
50422                 return [0, 0];
50423             break;
50424         }
50425     },
50426
50427     getExpandAdj : function(){
50428         var c = this.collapsedEl, cm = this.cmargins;
50429         switch(this.position){
50430             case "west":
50431                 return [-(cm.right+c.getWidth()+cm.left), 0];
50432             break;
50433             case "east":
50434                 return [cm.right+c.getWidth()+cm.left, 0];
50435             break;
50436             case "north":
50437                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50438             break;
50439             case "south":
50440                 return [0, cm.top+cm.bottom+c.getHeight()];
50441             break;
50442         }
50443     }
50444 });/*
50445  * Based on:
50446  * Ext JS Library 1.1.1
50447  * Copyright(c) 2006-2007, Ext JS, LLC.
50448  *
50449  * Originally Released Under LGPL - original licence link has changed is not relivant.
50450  *
50451  * Fork - LGPL
50452  * <script type="text/javascript">
50453  */
50454 /*
50455  * These classes are private internal classes
50456  */
50457 Roo.CenterLayoutRegion = function(mgr, config){
50458     Roo.LayoutRegion.call(this, mgr, config, "center");
50459     this.visible = true;
50460     this.minWidth = config.minWidth || 20;
50461     this.minHeight = config.minHeight || 20;
50462 };
50463
50464 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50465     hide : function(){
50466         // center panel can't be hidden
50467     },
50468     
50469     show : function(){
50470         // center panel can't be hidden
50471     },
50472     
50473     getMinWidth: function(){
50474         return this.minWidth;
50475     },
50476     
50477     getMinHeight: function(){
50478         return this.minHeight;
50479     }
50480 });
50481
50482
50483 Roo.NorthLayoutRegion = function(mgr, config){
50484     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50485     if(this.split){
50486         this.split.placement = Roo.SplitBar.TOP;
50487         this.split.orientation = Roo.SplitBar.VERTICAL;
50488         this.split.el.addClass("x-layout-split-v");
50489     }
50490     var size = config.initialSize || config.height;
50491     if(typeof size != "undefined"){
50492         this.el.setHeight(size);
50493     }
50494 };
50495 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50496     orientation: Roo.SplitBar.VERTICAL,
50497     getBox : function(){
50498         if(this.collapsed){
50499             return this.collapsedEl.getBox();
50500         }
50501         var box = this.el.getBox();
50502         if(this.split){
50503             box.height += this.split.el.getHeight();
50504         }
50505         return box;
50506     },
50507     
50508     updateBox : function(box){
50509         if(this.split && !this.collapsed){
50510             box.height -= this.split.el.getHeight();
50511             this.split.el.setLeft(box.x);
50512             this.split.el.setTop(box.y+box.height);
50513             this.split.el.setWidth(box.width);
50514         }
50515         if(this.collapsed){
50516             this.updateBody(box.width, null);
50517         }
50518         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50519     }
50520 });
50521
50522 Roo.SouthLayoutRegion = function(mgr, config){
50523     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50524     if(this.split){
50525         this.split.placement = Roo.SplitBar.BOTTOM;
50526         this.split.orientation = Roo.SplitBar.VERTICAL;
50527         this.split.el.addClass("x-layout-split-v");
50528     }
50529     var size = config.initialSize || config.height;
50530     if(typeof size != "undefined"){
50531         this.el.setHeight(size);
50532     }
50533 };
50534 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50535     orientation: Roo.SplitBar.VERTICAL,
50536     getBox : function(){
50537         if(this.collapsed){
50538             return this.collapsedEl.getBox();
50539         }
50540         var box = this.el.getBox();
50541         if(this.split){
50542             var sh = this.split.el.getHeight();
50543             box.height += sh;
50544             box.y -= sh;
50545         }
50546         return box;
50547     },
50548     
50549     updateBox : function(box){
50550         if(this.split && !this.collapsed){
50551             var sh = this.split.el.getHeight();
50552             box.height -= sh;
50553             box.y += sh;
50554             this.split.el.setLeft(box.x);
50555             this.split.el.setTop(box.y-sh);
50556             this.split.el.setWidth(box.width);
50557         }
50558         if(this.collapsed){
50559             this.updateBody(box.width, null);
50560         }
50561         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50562     }
50563 });
50564
50565 Roo.EastLayoutRegion = function(mgr, config){
50566     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50567     if(this.split){
50568         this.split.placement = Roo.SplitBar.RIGHT;
50569         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50570         this.split.el.addClass("x-layout-split-h");
50571     }
50572     var size = config.initialSize || config.width;
50573     if(typeof size != "undefined"){
50574         this.el.setWidth(size);
50575     }
50576 };
50577 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50578     orientation: Roo.SplitBar.HORIZONTAL,
50579     getBox : function(){
50580         if(this.collapsed){
50581             return this.collapsedEl.getBox();
50582         }
50583         var box = this.el.getBox();
50584         if(this.split){
50585             var sw = this.split.el.getWidth();
50586             box.width += sw;
50587             box.x -= sw;
50588         }
50589         return box;
50590     },
50591
50592     updateBox : function(box){
50593         if(this.split && !this.collapsed){
50594             var sw = this.split.el.getWidth();
50595             box.width -= sw;
50596             this.split.el.setLeft(box.x);
50597             this.split.el.setTop(box.y);
50598             this.split.el.setHeight(box.height);
50599             box.x += sw;
50600         }
50601         if(this.collapsed){
50602             this.updateBody(null, box.height);
50603         }
50604         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50605     }
50606 });
50607
50608 Roo.WestLayoutRegion = function(mgr, config){
50609     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50610     if(this.split){
50611         this.split.placement = Roo.SplitBar.LEFT;
50612         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50613         this.split.el.addClass("x-layout-split-h");
50614     }
50615     var size = config.initialSize || config.width;
50616     if(typeof size != "undefined"){
50617         this.el.setWidth(size);
50618     }
50619 };
50620 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50621     orientation: Roo.SplitBar.HORIZONTAL,
50622     getBox : function(){
50623         if(this.collapsed){
50624             return this.collapsedEl.getBox();
50625         }
50626         var box = this.el.getBox();
50627         if(this.split){
50628             box.width += this.split.el.getWidth();
50629         }
50630         return box;
50631     },
50632     
50633     updateBox : function(box){
50634         if(this.split && !this.collapsed){
50635             var sw = this.split.el.getWidth();
50636             box.width -= sw;
50637             this.split.el.setLeft(box.x+box.width);
50638             this.split.el.setTop(box.y);
50639             this.split.el.setHeight(box.height);
50640         }
50641         if(this.collapsed){
50642             this.updateBody(null, box.height);
50643         }
50644         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50645     }
50646 });
50647 /*
50648  * Based on:
50649  * Ext JS Library 1.1.1
50650  * Copyright(c) 2006-2007, Ext JS, LLC.
50651  *
50652  * Originally Released Under LGPL - original licence link has changed is not relivant.
50653  *
50654  * Fork - LGPL
50655  * <script type="text/javascript">
50656  */
50657  
50658  
50659 /*
50660  * Private internal class for reading and applying state
50661  */
50662 Roo.LayoutStateManager = function(layout){
50663      // default empty state
50664      this.state = {
50665         north: {},
50666         south: {},
50667         east: {},
50668         west: {}       
50669     };
50670 };
50671
50672 Roo.LayoutStateManager.prototype = {
50673     init : function(layout, provider){
50674         this.provider = provider;
50675         var state = provider.get(layout.id+"-layout-state");
50676         if(state){
50677             var wasUpdating = layout.isUpdating();
50678             if(!wasUpdating){
50679                 layout.beginUpdate();
50680             }
50681             for(var key in state){
50682                 if(typeof state[key] != "function"){
50683                     var rstate = state[key];
50684                     var r = layout.getRegion(key);
50685                     if(r && rstate){
50686                         if(rstate.size){
50687                             r.resizeTo(rstate.size);
50688                         }
50689                         if(rstate.collapsed == true){
50690                             r.collapse(true);
50691                         }else{
50692                             r.expand(null, true);
50693                         }
50694                     }
50695                 }
50696             }
50697             if(!wasUpdating){
50698                 layout.endUpdate();
50699             }
50700             this.state = state; 
50701         }
50702         this.layout = layout;
50703         layout.on("regionresized", this.onRegionResized, this);
50704         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50705         layout.on("regionexpanded", this.onRegionExpanded, this);
50706     },
50707     
50708     storeState : function(){
50709         this.provider.set(this.layout.id+"-layout-state", this.state);
50710     },
50711     
50712     onRegionResized : function(region, newSize){
50713         this.state[region.getPosition()].size = newSize;
50714         this.storeState();
50715     },
50716     
50717     onRegionCollapsed : function(region){
50718         this.state[region.getPosition()].collapsed = true;
50719         this.storeState();
50720     },
50721     
50722     onRegionExpanded : function(region){
50723         this.state[region.getPosition()].collapsed = false;
50724         this.storeState();
50725     }
50726 };/*
50727  * Based on:
50728  * Ext JS Library 1.1.1
50729  * Copyright(c) 2006-2007, Ext JS, LLC.
50730  *
50731  * Originally Released Under LGPL - original licence link has changed is not relivant.
50732  *
50733  * Fork - LGPL
50734  * <script type="text/javascript">
50735  */
50736 /**
50737  * @class Roo.ContentPanel
50738  * @extends Roo.util.Observable
50739  * A basic ContentPanel element.
50740  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50741  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50742  * @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
50743  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50744  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50745  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50746  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50747  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50748  * @cfg {String} title          The title for this panel
50749  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50750  * @cfg {String} url            Calls {@link #setUrl} with this value
50751  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50752  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50753  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50754  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50755
50756  * @constructor
50757  * Create a new ContentPanel.
50758  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50759  * @param {String/Object} config A string to set only the title or a config object
50760  * @param {String} content (optional) Set the HTML content for this panel
50761  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50762  */
50763 Roo.ContentPanel = function(el, config, content){
50764     
50765      
50766     /*
50767     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50768         config = el;
50769         el = Roo.id();
50770     }
50771     if (config && config.parentLayout) { 
50772         el = config.parentLayout.el.createChild(); 
50773     }
50774     */
50775     if(el.autoCreate){ // xtype is available if this is called from factory
50776         config = el;
50777         el = Roo.id();
50778     }
50779     this.el = Roo.get(el);
50780     if(!this.el && config && config.autoCreate){
50781         if(typeof config.autoCreate == "object"){
50782             if(!config.autoCreate.id){
50783                 config.autoCreate.id = config.id||el;
50784             }
50785             this.el = Roo.DomHelper.append(document.body,
50786                         config.autoCreate, true);
50787         }else{
50788             this.el = Roo.DomHelper.append(document.body,
50789                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50790         }
50791     }
50792     this.closable = false;
50793     this.loaded = false;
50794     this.active = false;
50795     if(typeof config == "string"){
50796         this.title = config;
50797     }else{
50798         Roo.apply(this, config);
50799     }
50800     
50801     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50802         this.wrapEl = this.el.wrap();
50803         this.toolbar.container = this.el.insertSibling(false, 'before');
50804         this.toolbar = new Roo.Toolbar(this.toolbar);
50805     }
50806     
50807     // xtype created footer. - not sure if will work as we normally have to render first..
50808     if (this.footer && !this.footer.el && this.footer.xtype) {
50809         if (!this.wrapEl) {
50810             this.wrapEl = this.el.wrap();
50811         }
50812     
50813         this.footer.container = this.wrapEl.createChild();
50814          
50815         this.footer = Roo.factory(this.footer, Roo);
50816         
50817     }
50818     
50819     if(this.resizeEl){
50820         this.resizeEl = Roo.get(this.resizeEl, true);
50821     }else{
50822         this.resizeEl = this.el;
50823     }
50824     // handle view.xtype
50825     
50826  
50827     
50828     
50829     this.addEvents({
50830         /**
50831          * @event activate
50832          * Fires when this panel is activated. 
50833          * @param {Roo.ContentPanel} this
50834          */
50835         "activate" : true,
50836         /**
50837          * @event deactivate
50838          * Fires when this panel is activated. 
50839          * @param {Roo.ContentPanel} this
50840          */
50841         "deactivate" : true,
50842
50843         /**
50844          * @event resize
50845          * Fires when this panel is resized if fitToFrame is true.
50846          * @param {Roo.ContentPanel} this
50847          * @param {Number} width The width after any component adjustments
50848          * @param {Number} height The height after any component adjustments
50849          */
50850         "resize" : true,
50851         
50852          /**
50853          * @event render
50854          * Fires when this tab is created
50855          * @param {Roo.ContentPanel} this
50856          */
50857         "render" : true
50858         
50859         
50860         
50861     });
50862     
50863
50864     
50865     
50866     if(this.autoScroll){
50867         this.resizeEl.setStyle("overflow", "auto");
50868     } else {
50869         // fix randome scrolling
50870         this.el.on('scroll', function() {
50871             Roo.log('fix random scolling');
50872             this.scrollTo('top',0); 
50873         });
50874     }
50875     content = content || this.content;
50876     if(content){
50877         this.setContent(content);
50878     }
50879     if(config && config.url){
50880         this.setUrl(this.url, this.params, this.loadOnce);
50881     }
50882     
50883     
50884     
50885     Roo.ContentPanel.superclass.constructor.call(this);
50886     
50887     if (this.view && typeof(this.view.xtype) != 'undefined') {
50888         this.view.el = this.el.appendChild(document.createElement("div"));
50889         this.view = Roo.factory(this.view); 
50890         this.view.render  &&  this.view.render(false, '');  
50891     }
50892     
50893     
50894     this.fireEvent('render', this);
50895 };
50896
50897 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50898     tabTip:'',
50899     setRegion : function(region){
50900         this.region = region;
50901         if(region){
50902            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50903         }else{
50904            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50905         } 
50906     },
50907     
50908     /**
50909      * Returns the toolbar for this Panel if one was configured. 
50910      * @return {Roo.Toolbar} 
50911      */
50912     getToolbar : function(){
50913         return this.toolbar;
50914     },
50915     
50916     setActiveState : function(active){
50917         this.active = active;
50918         if(!active){
50919             this.fireEvent("deactivate", this);
50920         }else{
50921             this.fireEvent("activate", this);
50922         }
50923     },
50924     /**
50925      * Updates this panel's element
50926      * @param {String} content The new content
50927      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50928     */
50929     setContent : function(content, loadScripts){
50930         this.el.update(content, loadScripts);
50931     },
50932
50933     ignoreResize : function(w, h){
50934         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50935             return true;
50936         }else{
50937             this.lastSize = {width: w, height: h};
50938             return false;
50939         }
50940     },
50941     /**
50942      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50943      * @return {Roo.UpdateManager} The UpdateManager
50944      */
50945     getUpdateManager : function(){
50946         return this.el.getUpdateManager();
50947     },
50948      /**
50949      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50950      * @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:
50951 <pre><code>
50952 panel.load({
50953     url: "your-url.php",
50954     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50955     callback: yourFunction,
50956     scope: yourObject, //(optional scope)
50957     discardUrl: false,
50958     nocache: false,
50959     text: "Loading...",
50960     timeout: 30,
50961     scripts: false
50962 });
50963 </code></pre>
50964      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50965      * 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.
50966      * @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}
50967      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50968      * @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.
50969      * @return {Roo.ContentPanel} this
50970      */
50971     load : function(){
50972         var um = this.el.getUpdateManager();
50973         um.update.apply(um, arguments);
50974         return this;
50975     },
50976
50977
50978     /**
50979      * 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.
50980      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50981      * @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)
50982      * @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)
50983      * @return {Roo.UpdateManager} The UpdateManager
50984      */
50985     setUrl : function(url, params, loadOnce){
50986         if(this.refreshDelegate){
50987             this.removeListener("activate", this.refreshDelegate);
50988         }
50989         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50990         this.on("activate", this.refreshDelegate);
50991         return this.el.getUpdateManager();
50992     },
50993     
50994     _handleRefresh : function(url, params, loadOnce){
50995         if(!loadOnce || !this.loaded){
50996             var updater = this.el.getUpdateManager();
50997             updater.update(url, params, this._setLoaded.createDelegate(this));
50998         }
50999     },
51000     
51001     _setLoaded : function(){
51002         this.loaded = true;
51003     }, 
51004     
51005     /**
51006      * Returns this panel's id
51007      * @return {String} 
51008      */
51009     getId : function(){
51010         return this.el.id;
51011     },
51012     
51013     /** 
51014      * Returns this panel's element - used by regiosn to add.
51015      * @return {Roo.Element} 
51016      */
51017     getEl : function(){
51018         return this.wrapEl || this.el;
51019     },
51020     
51021     adjustForComponents : function(width, height)
51022     {
51023         //Roo.log('adjustForComponents ');
51024         if(this.resizeEl != this.el){
51025             width -= this.el.getFrameWidth('lr');
51026             height -= this.el.getFrameWidth('tb');
51027         }
51028         if(this.toolbar){
51029             var te = this.toolbar.getEl();
51030             height -= te.getHeight();
51031             te.setWidth(width);
51032         }
51033         if(this.footer){
51034             var te = this.footer.getEl();
51035             Roo.log("footer:" + te.getHeight());
51036             
51037             height -= te.getHeight();
51038             te.setWidth(width);
51039         }
51040         
51041         
51042         if(this.adjustments){
51043             width += this.adjustments[0];
51044             height += this.adjustments[1];
51045         }
51046         return {"width": width, "height": height};
51047     },
51048     
51049     setSize : function(width, height){
51050         if(this.fitToFrame && !this.ignoreResize(width, height)){
51051             if(this.fitContainer && this.resizeEl != this.el){
51052                 this.el.setSize(width, height);
51053             }
51054             var size = this.adjustForComponents(width, height);
51055             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51056             this.fireEvent('resize', this, size.width, size.height);
51057         }
51058     },
51059     
51060     /**
51061      * Returns this panel's title
51062      * @return {String} 
51063      */
51064     getTitle : function(){
51065         return this.title;
51066     },
51067     
51068     /**
51069      * Set this panel's title
51070      * @param {String} title
51071      */
51072     setTitle : function(title){
51073         this.title = title;
51074         if(this.region){
51075             this.region.updatePanelTitle(this, title);
51076         }
51077     },
51078     
51079     /**
51080      * Returns true is this panel was configured to be closable
51081      * @return {Boolean} 
51082      */
51083     isClosable : function(){
51084         return this.closable;
51085     },
51086     
51087     beforeSlide : function(){
51088         this.el.clip();
51089         this.resizeEl.clip();
51090     },
51091     
51092     afterSlide : function(){
51093         this.el.unclip();
51094         this.resizeEl.unclip();
51095     },
51096     
51097     /**
51098      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51099      *   Will fail silently if the {@link #setUrl} method has not been called.
51100      *   This does not activate the panel, just updates its content.
51101      */
51102     refresh : function(){
51103         if(this.refreshDelegate){
51104            this.loaded = false;
51105            this.refreshDelegate();
51106         }
51107     },
51108     
51109     /**
51110      * Destroys this panel
51111      */
51112     destroy : function(){
51113         this.el.removeAllListeners();
51114         var tempEl = document.createElement("span");
51115         tempEl.appendChild(this.el.dom);
51116         tempEl.innerHTML = "";
51117         this.el.remove();
51118         this.el = null;
51119     },
51120     
51121     /**
51122      * form - if the content panel contains a form - this is a reference to it.
51123      * @type {Roo.form.Form}
51124      */
51125     form : false,
51126     /**
51127      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51128      *    This contains a reference to it.
51129      * @type {Roo.View}
51130      */
51131     view : false,
51132     
51133       /**
51134      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51135      * <pre><code>
51136
51137 layout.addxtype({
51138        xtype : 'Form',
51139        items: [ .... ]
51140    }
51141 );
51142
51143 </code></pre>
51144      * @param {Object} cfg Xtype definition of item to add.
51145      */
51146     
51147     addxtype : function(cfg) {
51148         // add form..
51149         if (cfg.xtype.match(/^Form$/)) {
51150             
51151             var el;
51152             //if (this.footer) {
51153             //    el = this.footer.container.insertSibling(false, 'before');
51154             //} else {
51155                 el = this.el.createChild();
51156             //}
51157
51158             this.form = new  Roo.form.Form(cfg);
51159             
51160             
51161             if ( this.form.allItems.length) this.form.render(el.dom);
51162             return this.form;
51163         }
51164         // should only have one of theses..
51165         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51166             // views.. should not be just added - used named prop 'view''
51167             
51168             cfg.el = this.el.appendChild(document.createElement("div"));
51169             // factory?
51170             
51171             var ret = new Roo.factory(cfg);
51172              
51173              ret.render && ret.render(false, ''); // render blank..
51174             this.view = ret;
51175             return ret;
51176         }
51177         return false;
51178     }
51179 });
51180
51181 /**
51182  * @class Roo.GridPanel
51183  * @extends Roo.ContentPanel
51184  * @constructor
51185  * Create a new GridPanel.
51186  * @param {Roo.grid.Grid} grid The grid for this panel
51187  * @param {String/Object} config A string to set only the panel's title, or a config object
51188  */
51189 Roo.GridPanel = function(grid, config){
51190     
51191   
51192     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51193         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51194         
51195     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51196     
51197     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51198     
51199     if(this.toolbar){
51200         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51201     }
51202     // xtype created footer. - not sure if will work as we normally have to render first..
51203     if (this.footer && !this.footer.el && this.footer.xtype) {
51204         
51205         this.footer.container = this.grid.getView().getFooterPanel(true);
51206         this.footer.dataSource = this.grid.dataSource;
51207         this.footer = Roo.factory(this.footer, Roo);
51208         
51209     }
51210     
51211     grid.monitorWindowResize = false; // turn off autosizing
51212     grid.autoHeight = false;
51213     grid.autoWidth = false;
51214     this.grid = grid;
51215     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51216 };
51217
51218 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51219     getId : function(){
51220         return this.grid.id;
51221     },
51222     
51223     /**
51224      * Returns the grid for this panel
51225      * @return {Roo.grid.Grid} 
51226      */
51227     getGrid : function(){
51228         return this.grid;    
51229     },
51230     
51231     setSize : function(width, height){
51232         if(!this.ignoreResize(width, height)){
51233             var grid = this.grid;
51234             var size = this.adjustForComponents(width, height);
51235             grid.getGridEl().setSize(size.width, size.height);
51236             grid.autoSize();
51237         }
51238     },
51239     
51240     beforeSlide : function(){
51241         this.grid.getView().scroller.clip();
51242     },
51243     
51244     afterSlide : function(){
51245         this.grid.getView().scroller.unclip();
51246     },
51247     
51248     destroy : function(){
51249         this.grid.destroy();
51250         delete this.grid;
51251         Roo.GridPanel.superclass.destroy.call(this); 
51252     }
51253 });
51254
51255
51256 /**
51257  * @class Roo.NestedLayoutPanel
51258  * @extends Roo.ContentPanel
51259  * @constructor
51260  * Create a new NestedLayoutPanel.
51261  * 
51262  * 
51263  * @param {Roo.BorderLayout} layout The layout for this panel
51264  * @param {String/Object} config A string to set only the title or a config object
51265  */
51266 Roo.NestedLayoutPanel = function(layout, config)
51267 {
51268     // construct with only one argument..
51269     /* FIXME - implement nicer consturctors
51270     if (layout.layout) {
51271         config = layout;
51272         layout = config.layout;
51273         delete config.layout;
51274     }
51275     if (layout.xtype && !layout.getEl) {
51276         // then layout needs constructing..
51277         layout = Roo.factory(layout, Roo);
51278     }
51279     */
51280     
51281     
51282     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51283     
51284     layout.monitorWindowResize = false; // turn off autosizing
51285     this.layout = layout;
51286     this.layout.getEl().addClass("x-layout-nested-layout");
51287     
51288     
51289     
51290     
51291 };
51292
51293 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51294
51295     setSize : function(width, height){
51296         if(!this.ignoreResize(width, height)){
51297             var size = this.adjustForComponents(width, height);
51298             var el = this.layout.getEl();
51299             el.setSize(size.width, size.height);
51300             var touch = el.dom.offsetWidth;
51301             this.layout.layout();
51302             // ie requires a double layout on the first pass
51303             if(Roo.isIE && !this.initialized){
51304                 this.initialized = true;
51305                 this.layout.layout();
51306             }
51307         }
51308     },
51309     
51310     // activate all subpanels if not currently active..
51311     
51312     setActiveState : function(active){
51313         this.active = active;
51314         if(!active){
51315             this.fireEvent("deactivate", this);
51316             return;
51317         }
51318         
51319         this.fireEvent("activate", this);
51320         // not sure if this should happen before or after..
51321         if (!this.layout) {
51322             return; // should not happen..
51323         }
51324         var reg = false;
51325         for (var r in this.layout.regions) {
51326             reg = this.layout.getRegion(r);
51327             if (reg.getActivePanel()) {
51328                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51329                 reg.setActivePanel(reg.getActivePanel());
51330                 continue;
51331             }
51332             if (!reg.panels.length) {
51333                 continue;
51334             }
51335             reg.showPanel(reg.getPanel(0));
51336         }
51337         
51338         
51339         
51340         
51341     },
51342     
51343     /**
51344      * Returns the nested BorderLayout for this panel
51345      * @return {Roo.BorderLayout} 
51346      */
51347     getLayout : function(){
51348         return this.layout;
51349     },
51350     
51351      /**
51352      * Adds a xtype elements to the layout of the nested panel
51353      * <pre><code>
51354
51355 panel.addxtype({
51356        xtype : 'ContentPanel',
51357        region: 'west',
51358        items: [ .... ]
51359    }
51360 );
51361
51362 panel.addxtype({
51363         xtype : 'NestedLayoutPanel',
51364         region: 'west',
51365         layout: {
51366            center: { },
51367            west: { }   
51368         },
51369         items : [ ... list of content panels or nested layout panels.. ]
51370    }
51371 );
51372 </code></pre>
51373      * @param {Object} cfg Xtype definition of item to add.
51374      */
51375     addxtype : function(cfg) {
51376         return this.layout.addxtype(cfg);
51377     
51378     }
51379 });
51380
51381 Roo.ScrollPanel = function(el, config, content){
51382     config = config || {};
51383     config.fitToFrame = true;
51384     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51385     
51386     this.el.dom.style.overflow = "hidden";
51387     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51388     this.el.removeClass("x-layout-inactive-content");
51389     this.el.on("mousewheel", this.onWheel, this);
51390
51391     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51392     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51393     up.unselectable(); down.unselectable();
51394     up.on("click", this.scrollUp, this);
51395     down.on("click", this.scrollDown, this);
51396     up.addClassOnOver("x-scroller-btn-over");
51397     down.addClassOnOver("x-scroller-btn-over");
51398     up.addClassOnClick("x-scroller-btn-click");
51399     down.addClassOnClick("x-scroller-btn-click");
51400     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51401
51402     this.resizeEl = this.el;
51403     this.el = wrap; this.up = up; this.down = down;
51404 };
51405
51406 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51407     increment : 100,
51408     wheelIncrement : 5,
51409     scrollUp : function(){
51410         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51411     },
51412
51413     scrollDown : function(){
51414         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51415     },
51416
51417     afterScroll : function(){
51418         var el = this.resizeEl;
51419         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51420         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51421         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51422     },
51423
51424     setSize : function(){
51425         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51426         this.afterScroll();
51427     },
51428
51429     onWheel : function(e){
51430         var d = e.getWheelDelta();
51431         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51432         this.afterScroll();
51433         e.stopEvent();
51434     },
51435
51436     setContent : function(content, loadScripts){
51437         this.resizeEl.update(content, loadScripts);
51438     }
51439
51440 });
51441
51442
51443
51444
51445
51446
51447
51448
51449
51450 /**
51451  * @class Roo.TreePanel
51452  * @extends Roo.ContentPanel
51453  * @constructor
51454  * Create a new TreePanel. - defaults to fit/scoll contents.
51455  * @param {String/Object} config A string to set only the panel's title, or a config object
51456  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51457  */
51458 Roo.TreePanel = function(config){
51459     var el = config.el;
51460     var tree = config.tree;
51461     delete config.tree; 
51462     delete config.el; // hopefull!
51463     
51464     // wrapper for IE7 strict & safari scroll issue
51465     
51466     var treeEl = el.createChild();
51467     config.resizeEl = treeEl;
51468     
51469     
51470     
51471     Roo.TreePanel.superclass.constructor.call(this, el, config);
51472  
51473  
51474     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51475     //console.log(tree);
51476     this.on('activate', function()
51477     {
51478         if (this.tree.rendered) {
51479             return;
51480         }
51481         //console.log('render tree');
51482         this.tree.render();
51483     });
51484     // this should not be needed.. - it's actually the 'el' that resizes?
51485     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51486     
51487     //this.on('resize',  function (cp, w, h) {
51488     //        this.tree.innerCt.setWidth(w);
51489     //        this.tree.innerCt.setHeight(h);
51490     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51491     //});
51492
51493         
51494     
51495 };
51496
51497 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51498     fitToFrame : true,
51499     autoScroll : true
51500 });
51501
51502
51503
51504
51505
51506
51507
51508
51509
51510
51511
51512 /*
51513  * Based on:
51514  * Ext JS Library 1.1.1
51515  * Copyright(c) 2006-2007, Ext JS, LLC.
51516  *
51517  * Originally Released Under LGPL - original licence link has changed is not relivant.
51518  *
51519  * Fork - LGPL
51520  * <script type="text/javascript">
51521  */
51522  
51523
51524 /**
51525  * @class Roo.ReaderLayout
51526  * @extends Roo.BorderLayout
51527  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51528  * center region containing two nested regions (a top one for a list view and one for item preview below),
51529  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51530  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51531  * expedites the setup of the overall layout and regions for this common application style.
51532  * Example:
51533  <pre><code>
51534 var reader = new Roo.ReaderLayout();
51535 var CP = Roo.ContentPanel;  // shortcut for adding
51536
51537 reader.beginUpdate();
51538 reader.add("north", new CP("north", "North"));
51539 reader.add("west", new CP("west", {title: "West"}));
51540 reader.add("east", new CP("east", {title: "East"}));
51541
51542 reader.regions.listView.add(new CP("listView", "List"));
51543 reader.regions.preview.add(new CP("preview", "Preview"));
51544 reader.endUpdate();
51545 </code></pre>
51546 * @constructor
51547 * Create a new ReaderLayout
51548 * @param {Object} config Configuration options
51549 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51550 * document.body if omitted)
51551 */
51552 Roo.ReaderLayout = function(config, renderTo){
51553     var c = config || {size:{}};
51554     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51555         north: c.north !== false ? Roo.apply({
51556             split:false,
51557             initialSize: 32,
51558             titlebar: false
51559         }, c.north) : false,
51560         west: c.west !== false ? Roo.apply({
51561             split:true,
51562             initialSize: 200,
51563             minSize: 175,
51564             maxSize: 400,
51565             titlebar: true,
51566             collapsible: true,
51567             animate: true,
51568             margins:{left:5,right:0,bottom:5,top:5},
51569             cmargins:{left:5,right:5,bottom:5,top:5}
51570         }, c.west) : false,
51571         east: c.east !== false ? Roo.apply({
51572             split:true,
51573             initialSize: 200,
51574             minSize: 175,
51575             maxSize: 400,
51576             titlebar: true,
51577             collapsible: true,
51578             animate: true,
51579             margins:{left:0,right:5,bottom:5,top:5},
51580             cmargins:{left:5,right:5,bottom:5,top:5}
51581         }, c.east) : false,
51582         center: Roo.apply({
51583             tabPosition: 'top',
51584             autoScroll:false,
51585             closeOnTab: true,
51586             titlebar:false,
51587             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51588         }, c.center)
51589     });
51590
51591     this.el.addClass('x-reader');
51592
51593     this.beginUpdate();
51594
51595     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51596         south: c.preview !== false ? Roo.apply({
51597             split:true,
51598             initialSize: 200,
51599             minSize: 100,
51600             autoScroll:true,
51601             collapsible:true,
51602             titlebar: true,
51603             cmargins:{top:5,left:0, right:0, bottom:0}
51604         }, c.preview) : false,
51605         center: Roo.apply({
51606             autoScroll:false,
51607             titlebar:false,
51608             minHeight:200
51609         }, c.listView)
51610     });
51611     this.add('center', new Roo.NestedLayoutPanel(inner,
51612             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51613
51614     this.endUpdate();
51615
51616     this.regions.preview = inner.getRegion('south');
51617     this.regions.listView = inner.getRegion('center');
51618 };
51619
51620 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51621  * Based on:
51622  * Ext JS Library 1.1.1
51623  * Copyright(c) 2006-2007, Ext JS, LLC.
51624  *
51625  * Originally Released Under LGPL - original licence link has changed is not relivant.
51626  *
51627  * Fork - LGPL
51628  * <script type="text/javascript">
51629  */
51630  
51631 /**
51632  * @class Roo.grid.Grid
51633  * @extends Roo.util.Observable
51634  * This class represents the primary interface of a component based grid control.
51635  * <br><br>Usage:<pre><code>
51636  var grid = new Roo.grid.Grid("my-container-id", {
51637      ds: myDataStore,
51638      cm: myColModel,
51639      selModel: mySelectionModel,
51640      autoSizeColumns: true,
51641      monitorWindowResize: false,
51642      trackMouseOver: true
51643  });
51644  // set any options
51645  grid.render();
51646  * </code></pre>
51647  * <b>Common Problems:</b><br/>
51648  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51649  * element will correct this<br/>
51650  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51651  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51652  * are unpredictable.<br/>
51653  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51654  * grid to calculate dimensions/offsets.<br/>
51655   * @constructor
51656  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51657  * The container MUST have some type of size defined for the grid to fill. The container will be
51658  * automatically set to position relative if it isn't already.
51659  * @param {Object} config A config object that sets properties on this grid.
51660  */
51661 Roo.grid.Grid = function(container, config){
51662         // initialize the container
51663         this.container = Roo.get(container);
51664         this.container.update("");
51665         this.container.setStyle("overflow", "hidden");
51666     this.container.addClass('x-grid-container');
51667
51668     this.id = this.container.id;
51669
51670     Roo.apply(this, config);
51671     // check and correct shorthanded configs
51672     if(this.ds){
51673         this.dataSource = this.ds;
51674         delete this.ds;
51675     }
51676     if(this.cm){
51677         this.colModel = this.cm;
51678         delete this.cm;
51679     }
51680     if(this.sm){
51681         this.selModel = this.sm;
51682         delete this.sm;
51683     }
51684
51685     if (this.selModel) {
51686         this.selModel = Roo.factory(this.selModel, Roo.grid);
51687         this.sm = this.selModel;
51688         this.sm.xmodule = this.xmodule || false;
51689     }
51690     if (typeof(this.colModel.config) == 'undefined') {
51691         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51692         this.cm = this.colModel;
51693         this.cm.xmodule = this.xmodule || false;
51694     }
51695     if (this.dataSource) {
51696         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51697         this.ds = this.dataSource;
51698         this.ds.xmodule = this.xmodule || false;
51699          
51700     }
51701     
51702     
51703     
51704     if(this.width){
51705         this.container.setWidth(this.width);
51706     }
51707
51708     if(this.height){
51709         this.container.setHeight(this.height);
51710     }
51711     /** @private */
51712         this.addEvents({
51713         // raw events
51714         /**
51715          * @event click
51716          * The raw click event for the entire grid.
51717          * @param {Roo.EventObject} e
51718          */
51719         "click" : true,
51720         /**
51721          * @event dblclick
51722          * The raw dblclick event for the entire grid.
51723          * @param {Roo.EventObject} e
51724          */
51725         "dblclick" : true,
51726         /**
51727          * @event contextmenu
51728          * The raw contextmenu event for the entire grid.
51729          * @param {Roo.EventObject} e
51730          */
51731         "contextmenu" : true,
51732         /**
51733          * @event mousedown
51734          * The raw mousedown event for the entire grid.
51735          * @param {Roo.EventObject} e
51736          */
51737         "mousedown" : true,
51738         /**
51739          * @event mouseup
51740          * The raw mouseup event for the entire grid.
51741          * @param {Roo.EventObject} e
51742          */
51743         "mouseup" : true,
51744         /**
51745          * @event mouseover
51746          * The raw mouseover event for the entire grid.
51747          * @param {Roo.EventObject} e
51748          */
51749         "mouseover" : true,
51750         /**
51751          * @event mouseout
51752          * The raw mouseout event for the entire grid.
51753          * @param {Roo.EventObject} e
51754          */
51755         "mouseout" : true,
51756         /**
51757          * @event keypress
51758          * The raw keypress event for the entire grid.
51759          * @param {Roo.EventObject} e
51760          */
51761         "keypress" : true,
51762         /**
51763          * @event keydown
51764          * The raw keydown event for the entire grid.
51765          * @param {Roo.EventObject} e
51766          */
51767         "keydown" : true,
51768
51769         // custom events
51770
51771         /**
51772          * @event cellclick
51773          * Fires when a cell is clicked
51774          * @param {Grid} this
51775          * @param {Number} rowIndex
51776          * @param {Number} columnIndex
51777          * @param {Roo.EventObject} e
51778          */
51779         "cellclick" : true,
51780         /**
51781          * @event celldblclick
51782          * Fires when a cell is double clicked
51783          * @param {Grid} this
51784          * @param {Number} rowIndex
51785          * @param {Number} columnIndex
51786          * @param {Roo.EventObject} e
51787          */
51788         "celldblclick" : true,
51789         /**
51790          * @event rowclick
51791          * Fires when a row is clicked
51792          * @param {Grid} this
51793          * @param {Number} rowIndex
51794          * @param {Roo.EventObject} e
51795          */
51796         "rowclick" : true,
51797         /**
51798          * @event rowdblclick
51799          * Fires when a row is double clicked
51800          * @param {Grid} this
51801          * @param {Number} rowIndex
51802          * @param {Roo.EventObject} e
51803          */
51804         "rowdblclick" : true,
51805         /**
51806          * @event headerclick
51807          * Fires when a header is clicked
51808          * @param {Grid} this
51809          * @param {Number} columnIndex
51810          * @param {Roo.EventObject} e
51811          */
51812         "headerclick" : true,
51813         /**
51814          * @event headerdblclick
51815          * Fires when a header cell is double clicked
51816          * @param {Grid} this
51817          * @param {Number} columnIndex
51818          * @param {Roo.EventObject} e
51819          */
51820         "headerdblclick" : true,
51821         /**
51822          * @event rowcontextmenu
51823          * Fires when a row is right clicked
51824          * @param {Grid} this
51825          * @param {Number} rowIndex
51826          * @param {Roo.EventObject} e
51827          */
51828         "rowcontextmenu" : true,
51829         /**
51830          * @event cellcontextmenu
51831          * Fires when a cell is right clicked
51832          * @param {Grid} this
51833          * @param {Number} rowIndex
51834          * @param {Number} cellIndex
51835          * @param {Roo.EventObject} e
51836          */
51837          "cellcontextmenu" : true,
51838         /**
51839          * @event headercontextmenu
51840          * Fires when a header is right clicked
51841          * @param {Grid} this
51842          * @param {Number} columnIndex
51843          * @param {Roo.EventObject} e
51844          */
51845         "headercontextmenu" : true,
51846         /**
51847          * @event bodyscroll
51848          * Fires when the body element is scrolled
51849          * @param {Number} scrollLeft
51850          * @param {Number} scrollTop
51851          */
51852         "bodyscroll" : true,
51853         /**
51854          * @event columnresize
51855          * Fires when the user resizes a column
51856          * @param {Number} columnIndex
51857          * @param {Number} newSize
51858          */
51859         "columnresize" : true,
51860         /**
51861          * @event columnmove
51862          * Fires when the user moves a column
51863          * @param {Number} oldIndex
51864          * @param {Number} newIndex
51865          */
51866         "columnmove" : true,
51867         /**
51868          * @event startdrag
51869          * Fires when row(s) start being dragged
51870          * @param {Grid} this
51871          * @param {Roo.GridDD} dd The drag drop object
51872          * @param {event} e The raw browser event
51873          */
51874         "startdrag" : true,
51875         /**
51876          * @event enddrag
51877          * Fires when a drag operation is complete
51878          * @param {Grid} this
51879          * @param {Roo.GridDD} dd The drag drop object
51880          * @param {event} e The raw browser event
51881          */
51882         "enddrag" : true,
51883         /**
51884          * @event dragdrop
51885          * Fires when dragged row(s) are dropped on a valid DD target
51886          * @param {Grid} this
51887          * @param {Roo.GridDD} dd The drag drop object
51888          * @param {String} targetId The target drag drop object
51889          * @param {event} e The raw browser event
51890          */
51891         "dragdrop" : true,
51892         /**
51893          * @event dragover
51894          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51895          * @param {Grid} this
51896          * @param {Roo.GridDD} dd The drag drop object
51897          * @param {String} targetId The target drag drop object
51898          * @param {event} e The raw browser event
51899          */
51900         "dragover" : true,
51901         /**
51902          * @event dragenter
51903          *  Fires when the dragged row(s) first cross another DD target while being dragged
51904          * @param {Grid} this
51905          * @param {Roo.GridDD} dd The drag drop object
51906          * @param {String} targetId The target drag drop object
51907          * @param {event} e The raw browser event
51908          */
51909         "dragenter" : true,
51910         /**
51911          * @event dragout
51912          * Fires when the dragged row(s) leave another DD target while being dragged
51913          * @param {Grid} this
51914          * @param {Roo.GridDD} dd The drag drop object
51915          * @param {String} targetId The target drag drop object
51916          * @param {event} e The raw browser event
51917          */
51918         "dragout" : true,
51919         /**
51920          * @event rowclass
51921          * Fires when a row is rendered, so you can change add a style to it.
51922          * @param {GridView} gridview   The grid view
51923          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51924          */
51925         'rowclass' : true,
51926
51927         /**
51928          * @event render
51929          * Fires when the grid is rendered
51930          * @param {Grid} grid
51931          */
51932         'render' : true
51933     });
51934
51935     Roo.grid.Grid.superclass.constructor.call(this);
51936 };
51937 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51938     
51939     /**
51940      * @cfg {String} ddGroup - drag drop group.
51941      */
51942
51943     /**
51944      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51945      */
51946     minColumnWidth : 25,
51947
51948     /**
51949      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51950      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51951      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51952      */
51953     autoSizeColumns : false,
51954
51955     /**
51956      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51957      */
51958     autoSizeHeaders : true,
51959
51960     /**
51961      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51962      */
51963     monitorWindowResize : true,
51964
51965     /**
51966      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51967      * rows measured to get a columns size. Default is 0 (all rows).
51968      */
51969     maxRowsToMeasure : 0,
51970
51971     /**
51972      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51973      */
51974     trackMouseOver : true,
51975
51976     /**
51977     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51978     */
51979     
51980     /**
51981     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51982     */
51983     enableDragDrop : false,
51984     
51985     /**
51986     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51987     */
51988     enableColumnMove : true,
51989     
51990     /**
51991     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51992     */
51993     enableColumnHide : true,
51994     
51995     /**
51996     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51997     */
51998     enableRowHeightSync : false,
51999     
52000     /**
52001     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52002     */
52003     stripeRows : true,
52004     
52005     /**
52006     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52007     */
52008     autoHeight : false,
52009
52010     /**
52011      * @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.
52012      */
52013     autoExpandColumn : false,
52014
52015     /**
52016     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52017     * Default is 50.
52018     */
52019     autoExpandMin : 50,
52020
52021     /**
52022     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52023     */
52024     autoExpandMax : 1000,
52025
52026     /**
52027     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52028     */
52029     view : null,
52030
52031     /**
52032     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52033     */
52034     loadMask : false,
52035     /**
52036     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52037     */
52038     dropTarget: false,
52039     
52040    
52041     
52042     // private
52043     rendered : false,
52044
52045     /**
52046     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52047     * of a fixed width. Default is false.
52048     */
52049     /**
52050     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52051     */
52052     /**
52053      * Called once after all setup has been completed and the grid is ready to be rendered.
52054      * @return {Roo.grid.Grid} this
52055      */
52056     render : function()
52057     {
52058         var c = this.container;
52059         // try to detect autoHeight/width mode
52060         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52061             this.autoHeight = true;
52062         }
52063         var view = this.getView();
52064         view.init(this);
52065
52066         c.on("click", this.onClick, this);
52067         c.on("dblclick", this.onDblClick, this);
52068         c.on("contextmenu", this.onContextMenu, this);
52069         c.on("keydown", this.onKeyDown, this);
52070         if (Roo.isTouch) {
52071             c.on("touchstart", this.onTouchStart, this);
52072         }
52073
52074         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52075
52076         this.getSelectionModel().init(this);
52077
52078         view.render();
52079
52080         if(this.loadMask){
52081             this.loadMask = new Roo.LoadMask(this.container,
52082                     Roo.apply({store:this.dataSource}, this.loadMask));
52083         }
52084         
52085         
52086         if (this.toolbar && this.toolbar.xtype) {
52087             this.toolbar.container = this.getView().getHeaderPanel(true);
52088             this.toolbar = new Roo.Toolbar(this.toolbar);
52089         }
52090         if (this.footer && this.footer.xtype) {
52091             this.footer.dataSource = this.getDataSource();
52092             this.footer.container = this.getView().getFooterPanel(true);
52093             this.footer = Roo.factory(this.footer, Roo);
52094         }
52095         if (this.dropTarget && this.dropTarget.xtype) {
52096             delete this.dropTarget.xtype;
52097             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52098         }
52099         
52100         
52101         this.rendered = true;
52102         this.fireEvent('render', this);
52103         return this;
52104     },
52105
52106         /**
52107          * Reconfigures the grid to use a different Store and Column Model.
52108          * The View will be bound to the new objects and refreshed.
52109          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52110          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52111          */
52112     reconfigure : function(dataSource, colModel){
52113         if(this.loadMask){
52114             this.loadMask.destroy();
52115             this.loadMask = new Roo.LoadMask(this.container,
52116                     Roo.apply({store:dataSource}, this.loadMask));
52117         }
52118         this.view.bind(dataSource, colModel);
52119         this.dataSource = dataSource;
52120         this.colModel = colModel;
52121         this.view.refresh(true);
52122     },
52123
52124     // private
52125     onKeyDown : function(e){
52126         this.fireEvent("keydown", e);
52127     },
52128
52129     /**
52130      * Destroy this grid.
52131      * @param {Boolean} removeEl True to remove the element
52132      */
52133     destroy : function(removeEl, keepListeners){
52134         if(this.loadMask){
52135             this.loadMask.destroy();
52136         }
52137         var c = this.container;
52138         c.removeAllListeners();
52139         this.view.destroy();
52140         this.colModel.purgeListeners();
52141         if(!keepListeners){
52142             this.purgeListeners();
52143         }
52144         c.update("");
52145         if(removeEl === true){
52146             c.remove();
52147         }
52148     },
52149
52150     // private
52151     processEvent : function(name, e){
52152         // does this fire select???
52153         Roo.log('grid:processEvent '  + name);
52154         
52155         if (name != 'touchstart' ) {
52156             this.fireEvent(name, e);    
52157         }
52158         
52159         var t = e.getTarget();
52160         var v = this.view;
52161         var header = v.findHeaderIndex(t);
52162         if(header !== false){
52163             var ename = name == 'touchstart' ? 'click' : name;
52164              
52165             this.fireEvent("header" + ename, this, header, e);
52166         }else{
52167             var row = v.findRowIndex(t);
52168             var cell = v.findCellIndex(t);
52169             if (name == 'touchstart') {
52170                 // first touch is always a click.
52171                 // hopefull this happens after selection is updated.?
52172                 name = false;
52173                 
52174                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52175                     var cs = this.selModel.getSelectedCell();
52176                     if (row == cs[0] && cell == cs[1]){
52177                         name = 'dblclick';
52178                     }
52179                 }
52180                 if (typeof(this.selModel.getSelections) != 'undefined') {
52181                     var cs = this.selModel.getSelections();
52182                     var ds = this.dataSource;
52183                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52184                         name = 'dblclick';
52185                     }
52186                 }
52187                 if (!name) {
52188                     return;
52189                 }
52190             }
52191             
52192             
52193             if(row !== false){
52194                 this.fireEvent("row" + name, this, row, e);
52195                 if(cell !== false){
52196                     this.fireEvent("cell" + name, this, row, cell, e);
52197                 }
52198             }
52199         }
52200     },
52201
52202     // private
52203     onClick : function(e){
52204         this.processEvent("click", e);
52205     },
52206    // private
52207     onTouchStart : function(e){
52208         this.processEvent("touchstart", e);
52209     },
52210
52211     // private
52212     onContextMenu : function(e, t){
52213         this.processEvent("contextmenu", e);
52214     },
52215
52216     // private
52217     onDblClick : function(e){
52218         this.processEvent("dblclick", e);
52219     },
52220
52221     // private
52222     walkCells : function(row, col, step, fn, scope){
52223         var cm = this.colModel, clen = cm.getColumnCount();
52224         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52225         if(step < 0){
52226             if(col < 0){
52227                 row--;
52228                 first = false;
52229             }
52230             while(row >= 0){
52231                 if(!first){
52232                     col = clen-1;
52233                 }
52234                 first = false;
52235                 while(col >= 0){
52236                     if(fn.call(scope || this, row, col, cm) === true){
52237                         return [row, col];
52238                     }
52239                     col--;
52240                 }
52241                 row--;
52242             }
52243         } else {
52244             if(col >= clen){
52245                 row++;
52246                 first = false;
52247             }
52248             while(row < rlen){
52249                 if(!first){
52250                     col = 0;
52251                 }
52252                 first = false;
52253                 while(col < clen){
52254                     if(fn.call(scope || this, row, col, cm) === true){
52255                         return [row, col];
52256                     }
52257                     col++;
52258                 }
52259                 row++;
52260             }
52261         }
52262         return null;
52263     },
52264
52265     // private
52266     getSelections : function(){
52267         return this.selModel.getSelections();
52268     },
52269
52270     /**
52271      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52272      * but if manual update is required this method will initiate it.
52273      */
52274     autoSize : function(){
52275         if(this.rendered){
52276             this.view.layout();
52277             if(this.view.adjustForScroll){
52278                 this.view.adjustForScroll();
52279             }
52280         }
52281     },
52282
52283     /**
52284      * Returns the grid's underlying element.
52285      * @return {Element} The element
52286      */
52287     getGridEl : function(){
52288         return this.container;
52289     },
52290
52291     // private for compatibility, overridden by editor grid
52292     stopEditing : function(){},
52293
52294     /**
52295      * Returns the grid's SelectionModel.
52296      * @return {SelectionModel}
52297      */
52298     getSelectionModel : function(){
52299         if(!this.selModel){
52300             this.selModel = new Roo.grid.RowSelectionModel();
52301         }
52302         return this.selModel;
52303     },
52304
52305     /**
52306      * Returns the grid's DataSource.
52307      * @return {DataSource}
52308      */
52309     getDataSource : function(){
52310         return this.dataSource;
52311     },
52312
52313     /**
52314      * Returns the grid's ColumnModel.
52315      * @return {ColumnModel}
52316      */
52317     getColumnModel : function(){
52318         return this.colModel;
52319     },
52320
52321     /**
52322      * Returns the grid's GridView object.
52323      * @return {GridView}
52324      */
52325     getView : function(){
52326         if(!this.view){
52327             this.view = new Roo.grid.GridView(this.viewConfig);
52328         }
52329         return this.view;
52330     },
52331     /**
52332      * Called to get grid's drag proxy text, by default returns this.ddText.
52333      * @return {String}
52334      */
52335     getDragDropText : function(){
52336         var count = this.selModel.getCount();
52337         return String.format(this.ddText, count, count == 1 ? '' : 's');
52338     }
52339 });
52340 /**
52341  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52342  * %0 is replaced with the number of selected rows.
52343  * @type String
52344  */
52345 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52346  * Based on:
52347  * Ext JS Library 1.1.1
52348  * Copyright(c) 2006-2007, Ext JS, LLC.
52349  *
52350  * Originally Released Under LGPL - original licence link has changed is not relivant.
52351  *
52352  * Fork - LGPL
52353  * <script type="text/javascript">
52354  */
52355  
52356 Roo.grid.AbstractGridView = function(){
52357         this.grid = null;
52358         
52359         this.events = {
52360             "beforerowremoved" : true,
52361             "beforerowsinserted" : true,
52362             "beforerefresh" : true,
52363             "rowremoved" : true,
52364             "rowsinserted" : true,
52365             "rowupdated" : true,
52366             "refresh" : true
52367         };
52368     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52369 };
52370
52371 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52372     rowClass : "x-grid-row",
52373     cellClass : "x-grid-cell",
52374     tdClass : "x-grid-td",
52375     hdClass : "x-grid-hd",
52376     splitClass : "x-grid-hd-split",
52377     
52378     init: function(grid){
52379         this.grid = grid;
52380                 var cid = this.grid.getGridEl().id;
52381         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52382         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52383         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52384         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52385         },
52386         
52387     getColumnRenderers : function(){
52388         var renderers = [];
52389         var cm = this.grid.colModel;
52390         var colCount = cm.getColumnCount();
52391         for(var i = 0; i < colCount; i++){
52392             renderers[i] = cm.getRenderer(i);
52393         }
52394         return renderers;
52395     },
52396     
52397     getColumnIds : function(){
52398         var ids = [];
52399         var cm = this.grid.colModel;
52400         var colCount = cm.getColumnCount();
52401         for(var i = 0; i < colCount; i++){
52402             ids[i] = cm.getColumnId(i);
52403         }
52404         return ids;
52405     },
52406     
52407     getDataIndexes : function(){
52408         if(!this.indexMap){
52409             this.indexMap = this.buildIndexMap();
52410         }
52411         return this.indexMap.colToData;
52412     },
52413     
52414     getColumnIndexByDataIndex : function(dataIndex){
52415         if(!this.indexMap){
52416             this.indexMap = this.buildIndexMap();
52417         }
52418         return this.indexMap.dataToCol[dataIndex];
52419     },
52420     
52421     /**
52422      * Set a css style for a column dynamically. 
52423      * @param {Number} colIndex The index of the column
52424      * @param {String} name The css property name
52425      * @param {String} value The css value
52426      */
52427     setCSSStyle : function(colIndex, name, value){
52428         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52429         Roo.util.CSS.updateRule(selector, name, value);
52430     },
52431     
52432     generateRules : function(cm){
52433         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52434         Roo.util.CSS.removeStyleSheet(rulesId);
52435         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52436             var cid = cm.getColumnId(i);
52437             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52438                          this.tdSelector, cid, " {\n}\n",
52439                          this.hdSelector, cid, " {\n}\n",
52440                          this.splitSelector, cid, " {\n}\n");
52441         }
52442         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52443     }
52444 });/*
52445  * Based on:
52446  * Ext JS Library 1.1.1
52447  * Copyright(c) 2006-2007, Ext JS, LLC.
52448  *
52449  * Originally Released Under LGPL - original licence link has changed is not relivant.
52450  *
52451  * Fork - LGPL
52452  * <script type="text/javascript">
52453  */
52454
52455 // private
52456 // This is a support class used internally by the Grid components
52457 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52458     this.grid = grid;
52459     this.view = grid.getView();
52460     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52461     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52462     if(hd2){
52463         this.setHandleElId(Roo.id(hd));
52464         this.setOuterHandleElId(Roo.id(hd2));
52465     }
52466     this.scroll = false;
52467 };
52468 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52469     maxDragWidth: 120,
52470     getDragData : function(e){
52471         var t = Roo.lib.Event.getTarget(e);
52472         var h = this.view.findHeaderCell(t);
52473         if(h){
52474             return {ddel: h.firstChild, header:h};
52475         }
52476         return false;
52477     },
52478
52479     onInitDrag : function(e){
52480         this.view.headersDisabled = true;
52481         var clone = this.dragData.ddel.cloneNode(true);
52482         clone.id = Roo.id();
52483         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52484         this.proxy.update(clone);
52485         return true;
52486     },
52487
52488     afterValidDrop : function(){
52489         var v = this.view;
52490         setTimeout(function(){
52491             v.headersDisabled = false;
52492         }, 50);
52493     },
52494
52495     afterInvalidDrop : function(){
52496         var v = this.view;
52497         setTimeout(function(){
52498             v.headersDisabled = false;
52499         }, 50);
52500     }
52501 });
52502 /*
52503  * Based on:
52504  * Ext JS Library 1.1.1
52505  * Copyright(c) 2006-2007, Ext JS, LLC.
52506  *
52507  * Originally Released Under LGPL - original licence link has changed is not relivant.
52508  *
52509  * Fork - LGPL
52510  * <script type="text/javascript">
52511  */
52512 // private
52513 // This is a support class used internally by the Grid components
52514 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52515     this.grid = grid;
52516     this.view = grid.getView();
52517     // split the proxies so they don't interfere with mouse events
52518     this.proxyTop = Roo.DomHelper.append(document.body, {
52519         cls:"col-move-top", html:"&#160;"
52520     }, true);
52521     this.proxyBottom = Roo.DomHelper.append(document.body, {
52522         cls:"col-move-bottom", html:"&#160;"
52523     }, true);
52524     this.proxyTop.hide = this.proxyBottom.hide = function(){
52525         this.setLeftTop(-100,-100);
52526         this.setStyle("visibility", "hidden");
52527     };
52528     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52529     // temporarily disabled
52530     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52531     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52532 };
52533 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52534     proxyOffsets : [-4, -9],
52535     fly: Roo.Element.fly,
52536
52537     getTargetFromEvent : function(e){
52538         var t = Roo.lib.Event.getTarget(e);
52539         var cindex = this.view.findCellIndex(t);
52540         if(cindex !== false){
52541             return this.view.getHeaderCell(cindex);
52542         }
52543         return null;
52544     },
52545
52546     nextVisible : function(h){
52547         var v = this.view, cm = this.grid.colModel;
52548         h = h.nextSibling;
52549         while(h){
52550             if(!cm.isHidden(v.getCellIndex(h))){
52551                 return h;
52552             }
52553             h = h.nextSibling;
52554         }
52555         return null;
52556     },
52557
52558     prevVisible : function(h){
52559         var v = this.view, cm = this.grid.colModel;
52560         h = h.prevSibling;
52561         while(h){
52562             if(!cm.isHidden(v.getCellIndex(h))){
52563                 return h;
52564             }
52565             h = h.prevSibling;
52566         }
52567         return null;
52568     },
52569
52570     positionIndicator : function(h, n, e){
52571         var x = Roo.lib.Event.getPageX(e);
52572         var r = Roo.lib.Dom.getRegion(n.firstChild);
52573         var px, pt, py = r.top + this.proxyOffsets[1];
52574         if((r.right - x) <= (r.right-r.left)/2){
52575             px = r.right+this.view.borderWidth;
52576             pt = "after";
52577         }else{
52578             px = r.left;
52579             pt = "before";
52580         }
52581         var oldIndex = this.view.getCellIndex(h);
52582         var newIndex = this.view.getCellIndex(n);
52583
52584         if(this.grid.colModel.isFixed(newIndex)){
52585             return false;
52586         }
52587
52588         var locked = this.grid.colModel.isLocked(newIndex);
52589
52590         if(pt == "after"){
52591             newIndex++;
52592         }
52593         if(oldIndex < newIndex){
52594             newIndex--;
52595         }
52596         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52597             return false;
52598         }
52599         px +=  this.proxyOffsets[0];
52600         this.proxyTop.setLeftTop(px, py);
52601         this.proxyTop.show();
52602         if(!this.bottomOffset){
52603             this.bottomOffset = this.view.mainHd.getHeight();
52604         }
52605         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52606         this.proxyBottom.show();
52607         return pt;
52608     },
52609
52610     onNodeEnter : function(n, dd, e, data){
52611         if(data.header != n){
52612             this.positionIndicator(data.header, n, e);
52613         }
52614     },
52615
52616     onNodeOver : function(n, dd, e, data){
52617         var result = false;
52618         if(data.header != n){
52619             result = this.positionIndicator(data.header, n, e);
52620         }
52621         if(!result){
52622             this.proxyTop.hide();
52623             this.proxyBottom.hide();
52624         }
52625         return result ? this.dropAllowed : this.dropNotAllowed;
52626     },
52627
52628     onNodeOut : function(n, dd, e, data){
52629         this.proxyTop.hide();
52630         this.proxyBottom.hide();
52631     },
52632
52633     onNodeDrop : function(n, dd, e, data){
52634         var h = data.header;
52635         if(h != n){
52636             var cm = this.grid.colModel;
52637             var x = Roo.lib.Event.getPageX(e);
52638             var r = Roo.lib.Dom.getRegion(n.firstChild);
52639             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52640             var oldIndex = this.view.getCellIndex(h);
52641             var newIndex = this.view.getCellIndex(n);
52642             var locked = cm.isLocked(newIndex);
52643             if(pt == "after"){
52644                 newIndex++;
52645             }
52646             if(oldIndex < newIndex){
52647                 newIndex--;
52648             }
52649             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52650                 return false;
52651             }
52652             cm.setLocked(oldIndex, locked, true);
52653             cm.moveColumn(oldIndex, newIndex);
52654             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52655             return true;
52656         }
52657         return false;
52658     }
52659 });
52660 /*
52661  * Based on:
52662  * Ext JS Library 1.1.1
52663  * Copyright(c) 2006-2007, Ext JS, LLC.
52664  *
52665  * Originally Released Under LGPL - original licence link has changed is not relivant.
52666  *
52667  * Fork - LGPL
52668  * <script type="text/javascript">
52669  */
52670   
52671 /**
52672  * @class Roo.grid.GridView
52673  * @extends Roo.util.Observable
52674  *
52675  * @constructor
52676  * @param {Object} config
52677  */
52678 Roo.grid.GridView = function(config){
52679     Roo.grid.GridView.superclass.constructor.call(this);
52680     this.el = null;
52681
52682     Roo.apply(this, config);
52683 };
52684
52685 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52686
52687     unselectable :  'unselectable="on"',
52688     unselectableCls :  'x-unselectable',
52689     
52690     
52691     rowClass : "x-grid-row",
52692
52693     cellClass : "x-grid-col",
52694
52695     tdClass : "x-grid-td",
52696
52697     hdClass : "x-grid-hd",
52698
52699     splitClass : "x-grid-split",
52700
52701     sortClasses : ["sort-asc", "sort-desc"],
52702
52703     enableMoveAnim : false,
52704
52705     hlColor: "C3DAF9",
52706
52707     dh : Roo.DomHelper,
52708
52709     fly : Roo.Element.fly,
52710
52711     css : Roo.util.CSS,
52712
52713     borderWidth: 1,
52714
52715     splitOffset: 3,
52716
52717     scrollIncrement : 22,
52718
52719     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52720
52721     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52722
52723     bind : function(ds, cm){
52724         if(this.ds){
52725             this.ds.un("load", this.onLoad, this);
52726             this.ds.un("datachanged", this.onDataChange, this);
52727             this.ds.un("add", this.onAdd, this);
52728             this.ds.un("remove", this.onRemove, this);
52729             this.ds.un("update", this.onUpdate, this);
52730             this.ds.un("clear", this.onClear, this);
52731         }
52732         if(ds){
52733             ds.on("load", this.onLoad, this);
52734             ds.on("datachanged", this.onDataChange, this);
52735             ds.on("add", this.onAdd, this);
52736             ds.on("remove", this.onRemove, this);
52737             ds.on("update", this.onUpdate, this);
52738             ds.on("clear", this.onClear, this);
52739         }
52740         this.ds = ds;
52741
52742         if(this.cm){
52743             this.cm.un("widthchange", this.onColWidthChange, this);
52744             this.cm.un("headerchange", this.onHeaderChange, this);
52745             this.cm.un("hiddenchange", this.onHiddenChange, this);
52746             this.cm.un("columnmoved", this.onColumnMove, this);
52747             this.cm.un("columnlockchange", this.onColumnLock, this);
52748         }
52749         if(cm){
52750             this.generateRules(cm);
52751             cm.on("widthchange", this.onColWidthChange, this);
52752             cm.on("headerchange", this.onHeaderChange, this);
52753             cm.on("hiddenchange", this.onHiddenChange, this);
52754             cm.on("columnmoved", this.onColumnMove, this);
52755             cm.on("columnlockchange", this.onColumnLock, this);
52756         }
52757         this.cm = cm;
52758     },
52759
52760     init: function(grid){
52761         Roo.grid.GridView.superclass.init.call(this, grid);
52762
52763         this.bind(grid.dataSource, grid.colModel);
52764
52765         grid.on("headerclick", this.handleHeaderClick, this);
52766
52767         if(grid.trackMouseOver){
52768             grid.on("mouseover", this.onRowOver, this);
52769             grid.on("mouseout", this.onRowOut, this);
52770         }
52771         grid.cancelTextSelection = function(){};
52772         this.gridId = grid.id;
52773
52774         var tpls = this.templates || {};
52775
52776         if(!tpls.master){
52777             tpls.master = new Roo.Template(
52778                '<div class="x-grid" hidefocus="true">',
52779                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52780                   '<div class="x-grid-topbar"></div>',
52781                   '<div class="x-grid-scroller"><div></div></div>',
52782                   '<div class="x-grid-locked">',
52783                       '<div class="x-grid-header">{lockedHeader}</div>',
52784                       '<div class="x-grid-body">{lockedBody}</div>',
52785                   "</div>",
52786                   '<div class="x-grid-viewport">',
52787                       '<div class="x-grid-header">{header}</div>',
52788                       '<div class="x-grid-body">{body}</div>',
52789                   "</div>",
52790                   '<div class="x-grid-bottombar"></div>',
52791                  
52792                   '<div class="x-grid-resize-proxy">&#160;</div>',
52793                "</div>"
52794             );
52795             tpls.master.disableformats = true;
52796         }
52797
52798         if(!tpls.header){
52799             tpls.header = new Roo.Template(
52800                '<table border="0" cellspacing="0" cellpadding="0">',
52801                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52802                "</table>{splits}"
52803             );
52804             tpls.header.disableformats = true;
52805         }
52806         tpls.header.compile();
52807
52808         if(!tpls.hcell){
52809             tpls.hcell = new Roo.Template(
52810                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52811                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52812                 "</div></td>"
52813              );
52814              tpls.hcell.disableFormats = true;
52815         }
52816         tpls.hcell.compile();
52817
52818         if(!tpls.hsplit){
52819             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52820                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52821             tpls.hsplit.disableFormats = true;
52822         }
52823         tpls.hsplit.compile();
52824
52825         if(!tpls.body){
52826             tpls.body = new Roo.Template(
52827                '<table border="0" cellspacing="0" cellpadding="0">',
52828                "<tbody>{rows}</tbody>",
52829                "</table>"
52830             );
52831             tpls.body.disableFormats = true;
52832         }
52833         tpls.body.compile();
52834
52835         if(!tpls.row){
52836             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52837             tpls.row.disableFormats = true;
52838         }
52839         tpls.row.compile();
52840
52841         if(!tpls.cell){
52842             tpls.cell = new Roo.Template(
52843                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52844                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52845                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52846                 "</td>"
52847             );
52848             tpls.cell.disableFormats = true;
52849         }
52850         tpls.cell.compile();
52851
52852         this.templates = tpls;
52853     },
52854
52855     // remap these for backwards compat
52856     onColWidthChange : function(){
52857         this.updateColumns.apply(this, arguments);
52858     },
52859     onHeaderChange : function(){
52860         this.updateHeaders.apply(this, arguments);
52861     }, 
52862     onHiddenChange : function(){
52863         this.handleHiddenChange.apply(this, arguments);
52864     },
52865     onColumnMove : function(){
52866         this.handleColumnMove.apply(this, arguments);
52867     },
52868     onColumnLock : function(){
52869         this.handleLockChange.apply(this, arguments);
52870     },
52871
52872     onDataChange : function(){
52873         this.refresh();
52874         this.updateHeaderSortState();
52875     },
52876
52877     onClear : function(){
52878         this.refresh();
52879     },
52880
52881     onUpdate : function(ds, record){
52882         this.refreshRow(record);
52883     },
52884
52885     refreshRow : function(record){
52886         var ds = this.ds, index;
52887         if(typeof record == 'number'){
52888             index = record;
52889             record = ds.getAt(index);
52890         }else{
52891             index = ds.indexOf(record);
52892         }
52893         this.insertRows(ds, index, index, true);
52894         this.onRemove(ds, record, index+1, true);
52895         this.syncRowHeights(index, index);
52896         this.layout();
52897         this.fireEvent("rowupdated", this, index, record);
52898     },
52899
52900     onAdd : function(ds, records, index){
52901         this.insertRows(ds, index, index + (records.length-1));
52902     },
52903
52904     onRemove : function(ds, record, index, isUpdate){
52905         if(isUpdate !== true){
52906             this.fireEvent("beforerowremoved", this, index, record);
52907         }
52908         var bt = this.getBodyTable(), lt = this.getLockedTable();
52909         if(bt.rows[index]){
52910             bt.firstChild.removeChild(bt.rows[index]);
52911         }
52912         if(lt.rows[index]){
52913             lt.firstChild.removeChild(lt.rows[index]);
52914         }
52915         if(isUpdate !== true){
52916             this.stripeRows(index);
52917             this.syncRowHeights(index, index);
52918             this.layout();
52919             this.fireEvent("rowremoved", this, index, record);
52920         }
52921     },
52922
52923     onLoad : function(){
52924         this.scrollToTop();
52925     },
52926
52927     /**
52928      * Scrolls the grid to the top
52929      */
52930     scrollToTop : function(){
52931         if(this.scroller){
52932             this.scroller.dom.scrollTop = 0;
52933             this.syncScroll();
52934         }
52935     },
52936
52937     /**
52938      * Gets a panel in the header of the grid that can be used for toolbars etc.
52939      * After modifying the contents of this panel a call to grid.autoSize() may be
52940      * required to register any changes in size.
52941      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52942      * @return Roo.Element
52943      */
52944     getHeaderPanel : function(doShow){
52945         if(doShow){
52946             this.headerPanel.show();
52947         }
52948         return this.headerPanel;
52949     },
52950
52951     /**
52952      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52953      * After modifying the contents of this panel a call to grid.autoSize() may be
52954      * required to register any changes in size.
52955      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52956      * @return Roo.Element
52957      */
52958     getFooterPanel : function(doShow){
52959         if(doShow){
52960             this.footerPanel.show();
52961         }
52962         return this.footerPanel;
52963     },
52964
52965     initElements : function(){
52966         var E = Roo.Element;
52967         var el = this.grid.getGridEl().dom.firstChild;
52968         var cs = el.childNodes;
52969
52970         this.el = new E(el);
52971         
52972          this.focusEl = new E(el.firstChild);
52973         this.focusEl.swallowEvent("click", true);
52974         
52975         this.headerPanel = new E(cs[1]);
52976         this.headerPanel.enableDisplayMode("block");
52977
52978         this.scroller = new E(cs[2]);
52979         this.scrollSizer = new E(this.scroller.dom.firstChild);
52980
52981         this.lockedWrap = new E(cs[3]);
52982         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52983         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52984
52985         this.mainWrap = new E(cs[4]);
52986         this.mainHd = new E(this.mainWrap.dom.firstChild);
52987         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52988
52989         this.footerPanel = new E(cs[5]);
52990         this.footerPanel.enableDisplayMode("block");
52991
52992         this.resizeProxy = new E(cs[6]);
52993
52994         this.headerSelector = String.format(
52995            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52996            this.lockedHd.id, this.mainHd.id
52997         );
52998
52999         this.splitterSelector = String.format(
53000            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53001            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53002         );
53003     },
53004     idToCssName : function(s)
53005     {
53006         return s.replace(/[^a-z0-9]+/ig, '-');
53007     },
53008
53009     getHeaderCell : function(index){
53010         return Roo.DomQuery.select(this.headerSelector)[index];
53011     },
53012
53013     getHeaderCellMeasure : function(index){
53014         return this.getHeaderCell(index).firstChild;
53015     },
53016
53017     getHeaderCellText : function(index){
53018         return this.getHeaderCell(index).firstChild.firstChild;
53019     },
53020
53021     getLockedTable : function(){
53022         return this.lockedBody.dom.firstChild;
53023     },
53024
53025     getBodyTable : function(){
53026         return this.mainBody.dom.firstChild;
53027     },
53028
53029     getLockedRow : function(index){
53030         return this.getLockedTable().rows[index];
53031     },
53032
53033     getRow : function(index){
53034         return this.getBodyTable().rows[index];
53035     },
53036
53037     getRowComposite : function(index){
53038         if(!this.rowEl){
53039             this.rowEl = new Roo.CompositeElementLite();
53040         }
53041         var els = [], lrow, mrow;
53042         if(lrow = this.getLockedRow(index)){
53043             els.push(lrow);
53044         }
53045         if(mrow = this.getRow(index)){
53046             els.push(mrow);
53047         }
53048         this.rowEl.elements = els;
53049         return this.rowEl;
53050     },
53051     /**
53052      * Gets the 'td' of the cell
53053      * 
53054      * @param {Integer} rowIndex row to select
53055      * @param {Integer} colIndex column to select
53056      * 
53057      * @return {Object} 
53058      */
53059     getCell : function(rowIndex, colIndex){
53060         var locked = this.cm.getLockedCount();
53061         var source;
53062         if(colIndex < locked){
53063             source = this.lockedBody.dom.firstChild;
53064         }else{
53065             source = this.mainBody.dom.firstChild;
53066             colIndex -= locked;
53067         }
53068         return source.rows[rowIndex].childNodes[colIndex];
53069     },
53070
53071     getCellText : function(rowIndex, colIndex){
53072         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53073     },
53074
53075     getCellBox : function(cell){
53076         var b = this.fly(cell).getBox();
53077         if(Roo.isOpera){ // opera fails to report the Y
53078             b.y = cell.offsetTop + this.mainBody.getY();
53079         }
53080         return b;
53081     },
53082
53083     getCellIndex : function(cell){
53084         var id = String(cell.className).match(this.cellRE);
53085         if(id){
53086             return parseInt(id[1], 10);
53087         }
53088         return 0;
53089     },
53090
53091     findHeaderIndex : function(n){
53092         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53093         return r ? this.getCellIndex(r) : false;
53094     },
53095
53096     findHeaderCell : function(n){
53097         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53098         return r ? r : false;
53099     },
53100
53101     findRowIndex : function(n){
53102         if(!n){
53103             return false;
53104         }
53105         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53106         return r ? r.rowIndex : false;
53107     },
53108
53109     findCellIndex : function(node){
53110         var stop = this.el.dom;
53111         while(node && node != stop){
53112             if(this.findRE.test(node.className)){
53113                 return this.getCellIndex(node);
53114             }
53115             node = node.parentNode;
53116         }
53117         return false;
53118     },
53119
53120     getColumnId : function(index){
53121         return this.cm.getColumnId(index);
53122     },
53123
53124     getSplitters : function()
53125     {
53126         if(this.splitterSelector){
53127            return Roo.DomQuery.select(this.splitterSelector);
53128         }else{
53129             return null;
53130       }
53131     },
53132
53133     getSplitter : function(index){
53134         return this.getSplitters()[index];
53135     },
53136
53137     onRowOver : function(e, t){
53138         var row;
53139         if((row = this.findRowIndex(t)) !== false){
53140             this.getRowComposite(row).addClass("x-grid-row-over");
53141         }
53142     },
53143
53144     onRowOut : function(e, t){
53145         var row;
53146         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53147             this.getRowComposite(row).removeClass("x-grid-row-over");
53148         }
53149     },
53150
53151     renderHeaders : function(){
53152         var cm = this.cm;
53153         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53154         var cb = [], lb = [], sb = [], lsb = [], p = {};
53155         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53156             p.cellId = "x-grid-hd-0-" + i;
53157             p.splitId = "x-grid-csplit-0-" + i;
53158             p.id = cm.getColumnId(i);
53159             p.title = cm.getColumnTooltip(i) || "";
53160             p.value = cm.getColumnHeader(i) || "";
53161             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53162             if(!cm.isLocked(i)){
53163                 cb[cb.length] = ct.apply(p);
53164                 sb[sb.length] = st.apply(p);
53165             }else{
53166                 lb[lb.length] = ct.apply(p);
53167                 lsb[lsb.length] = st.apply(p);
53168             }
53169         }
53170         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53171                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53172     },
53173
53174     updateHeaders : function(){
53175         var html = this.renderHeaders();
53176         this.lockedHd.update(html[0]);
53177         this.mainHd.update(html[1]);
53178     },
53179
53180     /**
53181      * Focuses the specified row.
53182      * @param {Number} row The row index
53183      */
53184     focusRow : function(row)
53185     {
53186         //Roo.log('GridView.focusRow');
53187         var x = this.scroller.dom.scrollLeft;
53188         this.focusCell(row, 0, false);
53189         this.scroller.dom.scrollLeft = x;
53190     },
53191
53192     /**
53193      * Focuses the specified cell.
53194      * @param {Number} row The row index
53195      * @param {Number} col The column index
53196      * @param {Boolean} hscroll false to disable horizontal scrolling
53197      */
53198     focusCell : function(row, col, hscroll)
53199     {
53200         //Roo.log('GridView.focusCell');
53201         var el = this.ensureVisible(row, col, hscroll);
53202         this.focusEl.alignTo(el, "tl-tl");
53203         if(Roo.isGecko){
53204             this.focusEl.focus();
53205         }else{
53206             this.focusEl.focus.defer(1, this.focusEl);
53207         }
53208     },
53209
53210     /**
53211      * Scrolls the specified cell into view
53212      * @param {Number} row The row index
53213      * @param {Number} col The column index
53214      * @param {Boolean} hscroll false to disable horizontal scrolling
53215      */
53216     ensureVisible : function(row, col, hscroll)
53217     {
53218         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53219         //return null; //disable for testing.
53220         if(typeof row != "number"){
53221             row = row.rowIndex;
53222         }
53223         if(row < 0 && row >= this.ds.getCount()){
53224             return  null;
53225         }
53226         col = (col !== undefined ? col : 0);
53227         var cm = this.grid.colModel;
53228         while(cm.isHidden(col)){
53229             col++;
53230         }
53231
53232         var el = this.getCell(row, col);
53233         if(!el){
53234             return null;
53235         }
53236         var c = this.scroller.dom;
53237
53238         var ctop = parseInt(el.offsetTop, 10);
53239         var cleft = parseInt(el.offsetLeft, 10);
53240         var cbot = ctop + el.offsetHeight;
53241         var cright = cleft + el.offsetWidth;
53242         
53243         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53244         var stop = parseInt(c.scrollTop, 10);
53245         var sleft = parseInt(c.scrollLeft, 10);
53246         var sbot = stop + ch;
53247         var sright = sleft + c.clientWidth;
53248         /*
53249         Roo.log('GridView.ensureVisible:' +
53250                 ' ctop:' + ctop +
53251                 ' c.clientHeight:' + c.clientHeight +
53252                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53253                 ' stop:' + stop +
53254                 ' cbot:' + cbot +
53255                 ' sbot:' + sbot +
53256                 ' ch:' + ch  
53257                 );
53258         */
53259         if(ctop < stop){
53260              c.scrollTop = ctop;
53261             //Roo.log("set scrolltop to ctop DISABLE?");
53262         }else if(cbot > sbot){
53263             //Roo.log("set scrolltop to cbot-ch");
53264             c.scrollTop = cbot-ch;
53265         }
53266         
53267         if(hscroll !== false){
53268             if(cleft < sleft){
53269                 c.scrollLeft = cleft;
53270             }else if(cright > sright){
53271                 c.scrollLeft = cright-c.clientWidth;
53272             }
53273         }
53274          
53275         return el;
53276     },
53277
53278     updateColumns : function(){
53279         this.grid.stopEditing();
53280         var cm = this.grid.colModel, colIds = this.getColumnIds();
53281         //var totalWidth = cm.getTotalWidth();
53282         var pos = 0;
53283         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53284             //if(cm.isHidden(i)) continue;
53285             var w = cm.getColumnWidth(i);
53286             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53287             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53288         }
53289         this.updateSplitters();
53290     },
53291
53292     generateRules : function(cm){
53293         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53294         Roo.util.CSS.removeStyleSheet(rulesId);
53295         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53296             var cid = cm.getColumnId(i);
53297             var align = '';
53298             if(cm.config[i].align){
53299                 align = 'text-align:'+cm.config[i].align+';';
53300             }
53301             var hidden = '';
53302             if(cm.isHidden(i)){
53303                 hidden = 'display:none;';
53304             }
53305             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53306             ruleBuf.push(
53307                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53308                     this.hdSelector, cid, " {\n", align, width, "}\n",
53309                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53310                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53311         }
53312         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53313     },
53314
53315     updateSplitters : function(){
53316         var cm = this.cm, s = this.getSplitters();
53317         if(s){ // splitters not created yet
53318             var pos = 0, locked = true;
53319             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53320                 if(cm.isHidden(i)) continue;
53321                 var w = cm.getColumnWidth(i); // make sure it's a number
53322                 if(!cm.isLocked(i) && locked){
53323                     pos = 0;
53324                     locked = false;
53325                 }
53326                 pos += w;
53327                 s[i].style.left = (pos-this.splitOffset) + "px";
53328             }
53329         }
53330     },
53331
53332     handleHiddenChange : function(colModel, colIndex, hidden){
53333         if(hidden){
53334             this.hideColumn(colIndex);
53335         }else{
53336             this.unhideColumn(colIndex);
53337         }
53338     },
53339
53340     hideColumn : function(colIndex){
53341         var cid = this.getColumnId(colIndex);
53342         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53343         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53344         if(Roo.isSafari){
53345             this.updateHeaders();
53346         }
53347         this.updateSplitters();
53348         this.layout();
53349     },
53350
53351     unhideColumn : function(colIndex){
53352         var cid = this.getColumnId(colIndex);
53353         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53354         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53355
53356         if(Roo.isSafari){
53357             this.updateHeaders();
53358         }
53359         this.updateSplitters();
53360         this.layout();
53361     },
53362
53363     insertRows : function(dm, firstRow, lastRow, isUpdate){
53364         if(firstRow == 0 && lastRow == dm.getCount()-1){
53365             this.refresh();
53366         }else{
53367             if(!isUpdate){
53368                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53369             }
53370             var s = this.getScrollState();
53371             var markup = this.renderRows(firstRow, lastRow);
53372             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53373             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53374             this.restoreScroll(s);
53375             if(!isUpdate){
53376                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53377                 this.syncRowHeights(firstRow, lastRow);
53378                 this.stripeRows(firstRow);
53379                 this.layout();
53380             }
53381         }
53382     },
53383
53384     bufferRows : function(markup, target, index){
53385         var before = null, trows = target.rows, tbody = target.tBodies[0];
53386         if(index < trows.length){
53387             before = trows[index];
53388         }
53389         var b = document.createElement("div");
53390         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53391         var rows = b.firstChild.rows;
53392         for(var i = 0, len = rows.length; i < len; i++){
53393             if(before){
53394                 tbody.insertBefore(rows[0], before);
53395             }else{
53396                 tbody.appendChild(rows[0]);
53397             }
53398         }
53399         b.innerHTML = "";
53400         b = null;
53401     },
53402
53403     deleteRows : function(dm, firstRow, lastRow){
53404         if(dm.getRowCount()<1){
53405             this.fireEvent("beforerefresh", this);
53406             this.mainBody.update("");
53407             this.lockedBody.update("");
53408             this.fireEvent("refresh", this);
53409         }else{
53410             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53411             var bt = this.getBodyTable();
53412             var tbody = bt.firstChild;
53413             var rows = bt.rows;
53414             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53415                 tbody.removeChild(rows[firstRow]);
53416             }
53417             this.stripeRows(firstRow);
53418             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53419         }
53420     },
53421
53422     updateRows : function(dataSource, firstRow, lastRow){
53423         var s = this.getScrollState();
53424         this.refresh();
53425         this.restoreScroll(s);
53426     },
53427
53428     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53429         if(!noRefresh){
53430            this.refresh();
53431         }
53432         this.updateHeaderSortState();
53433     },
53434
53435     getScrollState : function(){
53436         
53437         var sb = this.scroller.dom;
53438         return {left: sb.scrollLeft, top: sb.scrollTop};
53439     },
53440
53441     stripeRows : function(startRow){
53442         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53443             return;
53444         }
53445         startRow = startRow || 0;
53446         var rows = this.getBodyTable().rows;
53447         var lrows = this.getLockedTable().rows;
53448         var cls = ' x-grid-row-alt ';
53449         for(var i = startRow, len = rows.length; i < len; i++){
53450             var row = rows[i], lrow = lrows[i];
53451             var isAlt = ((i+1) % 2 == 0);
53452             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53453             if(isAlt == hasAlt){
53454                 continue;
53455             }
53456             if(isAlt){
53457                 row.className += " x-grid-row-alt";
53458             }else{
53459                 row.className = row.className.replace("x-grid-row-alt", "");
53460             }
53461             if(lrow){
53462                 lrow.className = row.className;
53463             }
53464         }
53465     },
53466
53467     restoreScroll : function(state){
53468         //Roo.log('GridView.restoreScroll');
53469         var sb = this.scroller.dom;
53470         sb.scrollLeft = state.left;
53471         sb.scrollTop = state.top;
53472         this.syncScroll();
53473     },
53474
53475     syncScroll : function(){
53476         //Roo.log('GridView.syncScroll');
53477         var sb = this.scroller.dom;
53478         var sh = this.mainHd.dom;
53479         var bs = this.mainBody.dom;
53480         var lv = this.lockedBody.dom;
53481         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53482         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53483     },
53484
53485     handleScroll : function(e){
53486         this.syncScroll();
53487         var sb = this.scroller.dom;
53488         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53489         e.stopEvent();
53490     },
53491
53492     handleWheel : function(e){
53493         var d = e.getWheelDelta();
53494         this.scroller.dom.scrollTop -= d*22;
53495         // set this here to prevent jumpy scrolling on large tables
53496         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53497         e.stopEvent();
53498     },
53499
53500     renderRows : function(startRow, endRow){
53501         // pull in all the crap needed to render rows
53502         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53503         var colCount = cm.getColumnCount();
53504
53505         if(ds.getCount() < 1){
53506             return ["", ""];
53507         }
53508
53509         // build a map for all the columns
53510         var cs = [];
53511         for(var i = 0; i < colCount; i++){
53512             var name = cm.getDataIndex(i);
53513             cs[i] = {
53514                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53515                 renderer : cm.getRenderer(i),
53516                 id : cm.getColumnId(i),
53517                 locked : cm.isLocked(i)
53518             };
53519         }
53520
53521         startRow = startRow || 0;
53522         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53523
53524         // records to render
53525         var rs = ds.getRange(startRow, endRow);
53526
53527         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53528     },
53529
53530     // As much as I hate to duplicate code, this was branched because FireFox really hates
53531     // [].join("") on strings. The performance difference was substantial enough to
53532     // branch this function
53533     doRender : Roo.isGecko ?
53534             function(cs, rs, ds, startRow, colCount, stripe){
53535                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53536                 // buffers
53537                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53538                 
53539                 var hasListener = this.grid.hasListener('rowclass');
53540                 var rowcfg = {};
53541                 for(var j = 0, len = rs.length; j < len; j++){
53542                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53543                     for(var i = 0; i < colCount; i++){
53544                         c = cs[i];
53545                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53546                         p.id = c.id;
53547                         p.css = p.attr = "";
53548                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53549                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53550                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53551                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53552                         }
53553                         var markup = ct.apply(p);
53554                         if(!c.locked){
53555                             cb+= markup;
53556                         }else{
53557                             lcb+= markup;
53558                         }
53559                     }
53560                     var alt = [];
53561                     if(stripe && ((rowIndex+1) % 2 == 0)){
53562                         alt.push("x-grid-row-alt")
53563                     }
53564                     if(r.dirty){
53565                         alt.push(  " x-grid-dirty-row");
53566                     }
53567                     rp.cells = lcb;
53568                     if(this.getRowClass){
53569                         alt.push(this.getRowClass(r, rowIndex));
53570                     }
53571                     if (hasListener) {
53572                         rowcfg = {
53573                              
53574                             record: r,
53575                             rowIndex : rowIndex,
53576                             rowClass : ''
53577                         }
53578                         this.grid.fireEvent('rowclass', this, rowcfg);
53579                         alt.push(rowcfg.rowClass);
53580                     }
53581                     rp.alt = alt.join(" ");
53582                     lbuf+= rt.apply(rp);
53583                     rp.cells = cb;
53584                     buf+=  rt.apply(rp);
53585                 }
53586                 return [lbuf, buf];
53587             } :
53588             function(cs, rs, ds, startRow, colCount, stripe){
53589                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53590                 // buffers
53591                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53592                 var hasListener = this.grid.hasListener('rowclass');
53593  
53594                 var rowcfg = {};
53595                 for(var j = 0, len = rs.length; j < len; j++){
53596                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53597                     for(var i = 0; i < colCount; i++){
53598                         c = cs[i];
53599                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53600                         p.id = c.id;
53601                         p.css = p.attr = "";
53602                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53603                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53604                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53605                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53606                         }
53607                         
53608                         var markup = ct.apply(p);
53609                         if(!c.locked){
53610                             cb[cb.length] = markup;
53611                         }else{
53612                             lcb[lcb.length] = markup;
53613                         }
53614                     }
53615                     var alt = [];
53616                     if(stripe && ((rowIndex+1) % 2 == 0)){
53617                         alt.push( "x-grid-row-alt");
53618                     }
53619                     if(r.dirty){
53620                         alt.push(" x-grid-dirty-row");
53621                     }
53622                     rp.cells = lcb;
53623                     if(this.getRowClass){
53624                         alt.push( this.getRowClass(r, rowIndex));
53625                     }
53626                     if (hasListener) {
53627                         rowcfg = {
53628                              
53629                             record: r,
53630                             rowIndex : rowIndex,
53631                             rowClass : ''
53632                         }
53633                         this.grid.fireEvent('rowclass', this, rowcfg);
53634                         alt.push(rowcfg.rowClass);
53635                     }
53636                     rp.alt = alt.join(" ");
53637                     rp.cells = lcb.join("");
53638                     lbuf[lbuf.length] = rt.apply(rp);
53639                     rp.cells = cb.join("");
53640                     buf[buf.length] =  rt.apply(rp);
53641                 }
53642                 return [lbuf.join(""), buf.join("")];
53643             },
53644
53645     renderBody : function(){
53646         var markup = this.renderRows();
53647         var bt = this.templates.body;
53648         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53649     },
53650
53651     /**
53652      * Refreshes the grid
53653      * @param {Boolean} headersToo
53654      */
53655     refresh : function(headersToo){
53656         this.fireEvent("beforerefresh", this);
53657         this.grid.stopEditing();
53658         var result = this.renderBody();
53659         this.lockedBody.update(result[0]);
53660         this.mainBody.update(result[1]);
53661         if(headersToo === true){
53662             this.updateHeaders();
53663             this.updateColumns();
53664             this.updateSplitters();
53665             this.updateHeaderSortState();
53666         }
53667         this.syncRowHeights();
53668         this.layout();
53669         this.fireEvent("refresh", this);
53670     },
53671
53672     handleColumnMove : function(cm, oldIndex, newIndex){
53673         this.indexMap = null;
53674         var s = this.getScrollState();
53675         this.refresh(true);
53676         this.restoreScroll(s);
53677         this.afterMove(newIndex);
53678     },
53679
53680     afterMove : function(colIndex){
53681         if(this.enableMoveAnim && Roo.enableFx){
53682             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53683         }
53684         // if multisort - fix sortOrder, and reload..
53685         if (this.grid.dataSource.multiSort) {
53686             // the we can call sort again..
53687             var dm = this.grid.dataSource;
53688             var cm = this.grid.colModel;
53689             var so = [];
53690             for(var i = 0; i < cm.config.length; i++ ) {
53691                 
53692                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53693                     continue; // dont' bother, it's not in sort list or being set.
53694                 }
53695                 
53696                 so.push(cm.config[i].dataIndex);
53697             };
53698             dm.sortOrder = so;
53699             dm.load(dm.lastOptions);
53700             
53701             
53702         }
53703         
53704     },
53705
53706     updateCell : function(dm, rowIndex, dataIndex){
53707         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53708         if(typeof colIndex == "undefined"){ // not present in grid
53709             return;
53710         }
53711         var cm = this.grid.colModel;
53712         var cell = this.getCell(rowIndex, colIndex);
53713         var cellText = this.getCellText(rowIndex, colIndex);
53714
53715         var p = {
53716             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53717             id : cm.getColumnId(colIndex),
53718             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53719         };
53720         var renderer = cm.getRenderer(colIndex);
53721         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53722         if(typeof val == "undefined" || val === "") val = "&#160;";
53723         cellText.innerHTML = val;
53724         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53725         this.syncRowHeights(rowIndex, rowIndex);
53726     },
53727
53728     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53729         var maxWidth = 0;
53730         if(this.grid.autoSizeHeaders){
53731             var h = this.getHeaderCellMeasure(colIndex);
53732             maxWidth = Math.max(maxWidth, h.scrollWidth);
53733         }
53734         var tb, index;
53735         if(this.cm.isLocked(colIndex)){
53736             tb = this.getLockedTable();
53737             index = colIndex;
53738         }else{
53739             tb = this.getBodyTable();
53740             index = colIndex - this.cm.getLockedCount();
53741         }
53742         if(tb && tb.rows){
53743             var rows = tb.rows;
53744             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53745             for(var i = 0; i < stopIndex; i++){
53746                 var cell = rows[i].childNodes[index].firstChild;
53747                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53748             }
53749         }
53750         return maxWidth + /*margin for error in IE*/ 5;
53751     },
53752     /**
53753      * Autofit a column to its content.
53754      * @param {Number} colIndex
53755      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53756      */
53757      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53758          if(this.cm.isHidden(colIndex)){
53759              return; // can't calc a hidden column
53760          }
53761         if(forceMinSize){
53762             var cid = this.cm.getColumnId(colIndex);
53763             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53764            if(this.grid.autoSizeHeaders){
53765                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53766            }
53767         }
53768         var newWidth = this.calcColumnWidth(colIndex);
53769         this.cm.setColumnWidth(colIndex,
53770             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53771         if(!suppressEvent){
53772             this.grid.fireEvent("columnresize", colIndex, newWidth);
53773         }
53774     },
53775
53776     /**
53777      * Autofits all columns to their content and then expands to fit any extra space in the grid
53778      */
53779      autoSizeColumns : function(){
53780         var cm = this.grid.colModel;
53781         var colCount = cm.getColumnCount();
53782         for(var i = 0; i < colCount; i++){
53783             this.autoSizeColumn(i, true, true);
53784         }
53785         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53786             this.fitColumns();
53787         }else{
53788             this.updateColumns();
53789             this.layout();
53790         }
53791     },
53792
53793     /**
53794      * Autofits all columns to the grid's width proportionate with their current size
53795      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53796      */
53797     fitColumns : function(reserveScrollSpace){
53798         var cm = this.grid.colModel;
53799         var colCount = cm.getColumnCount();
53800         var cols = [];
53801         var width = 0;
53802         var i, w;
53803         for (i = 0; i < colCount; i++){
53804             if(!cm.isHidden(i) && !cm.isFixed(i)){
53805                 w = cm.getColumnWidth(i);
53806                 cols.push(i);
53807                 cols.push(w);
53808                 width += w;
53809             }
53810         }
53811         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53812         if(reserveScrollSpace){
53813             avail -= 17;
53814         }
53815         var frac = (avail - cm.getTotalWidth())/width;
53816         while (cols.length){
53817             w = cols.pop();
53818             i = cols.pop();
53819             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53820         }
53821         this.updateColumns();
53822         this.layout();
53823     },
53824
53825     onRowSelect : function(rowIndex){
53826         var row = this.getRowComposite(rowIndex);
53827         row.addClass("x-grid-row-selected");
53828     },
53829
53830     onRowDeselect : function(rowIndex){
53831         var row = this.getRowComposite(rowIndex);
53832         row.removeClass("x-grid-row-selected");
53833     },
53834
53835     onCellSelect : function(row, col){
53836         var cell = this.getCell(row, col);
53837         if(cell){
53838             Roo.fly(cell).addClass("x-grid-cell-selected");
53839         }
53840     },
53841
53842     onCellDeselect : function(row, col){
53843         var cell = this.getCell(row, col);
53844         if(cell){
53845             Roo.fly(cell).removeClass("x-grid-cell-selected");
53846         }
53847     },
53848
53849     updateHeaderSortState : function(){
53850         
53851         // sort state can be single { field: xxx, direction : yyy}
53852         // or   { xxx=>ASC , yyy : DESC ..... }
53853         
53854         var mstate = {};
53855         if (!this.ds.multiSort) { 
53856             var state = this.ds.getSortState();
53857             if(!state){
53858                 return;
53859             }
53860             mstate[state.field] = state.direction;
53861             // FIXME... - this is not used here.. but might be elsewhere..
53862             this.sortState = state;
53863             
53864         } else {
53865             mstate = this.ds.sortToggle;
53866         }
53867         //remove existing sort classes..
53868         
53869         var sc = this.sortClasses;
53870         var hds = this.el.select(this.headerSelector).removeClass(sc);
53871         
53872         for(var f in mstate) {
53873         
53874             var sortColumn = this.cm.findColumnIndex(f);
53875             
53876             if(sortColumn != -1){
53877                 var sortDir = mstate[f];        
53878                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53879             }
53880         }
53881         
53882          
53883         
53884     },
53885
53886
53887     handleHeaderClick : function(g, index,e){
53888         
53889         Roo.log("header click");
53890         
53891         if (Roo.isTouch) {
53892             // touch events on header are handled by context
53893             this.handleHdCtx(g,index,e);
53894             return;
53895         }
53896         
53897         
53898         if(this.headersDisabled){
53899             return;
53900         }
53901         var dm = g.dataSource, cm = g.colModel;
53902         if(!cm.isSortable(index)){
53903             return;
53904         }
53905         g.stopEditing();
53906         
53907         if (dm.multiSort) {
53908             // update the sortOrder
53909             var so = [];
53910             for(var i = 0; i < cm.config.length; i++ ) {
53911                 
53912                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53913                     continue; // dont' bother, it's not in sort list or being set.
53914                 }
53915                 
53916                 so.push(cm.config[i].dataIndex);
53917             };
53918             dm.sortOrder = so;
53919         }
53920         
53921         
53922         dm.sort(cm.getDataIndex(index));
53923     },
53924
53925
53926     destroy : function(){
53927         if(this.colMenu){
53928             this.colMenu.removeAll();
53929             Roo.menu.MenuMgr.unregister(this.colMenu);
53930             this.colMenu.getEl().remove();
53931             delete this.colMenu;
53932         }
53933         if(this.hmenu){
53934             this.hmenu.removeAll();
53935             Roo.menu.MenuMgr.unregister(this.hmenu);
53936             this.hmenu.getEl().remove();
53937             delete this.hmenu;
53938         }
53939         if(this.grid.enableColumnMove){
53940             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53941             if(dds){
53942                 for(var dd in dds){
53943                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53944                         var elid = dds[dd].dragElId;
53945                         dds[dd].unreg();
53946                         Roo.get(elid).remove();
53947                     } else if(dds[dd].config.isTarget){
53948                         dds[dd].proxyTop.remove();
53949                         dds[dd].proxyBottom.remove();
53950                         dds[dd].unreg();
53951                     }
53952                     if(Roo.dd.DDM.locationCache[dd]){
53953                         delete Roo.dd.DDM.locationCache[dd];
53954                     }
53955                 }
53956                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53957             }
53958         }
53959         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53960         this.bind(null, null);
53961         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53962     },
53963
53964     handleLockChange : function(){
53965         this.refresh(true);
53966     },
53967
53968     onDenyColumnLock : function(){
53969
53970     },
53971
53972     onDenyColumnHide : function(){
53973
53974     },
53975
53976     handleHdMenuClick : function(item){
53977         var index = this.hdCtxIndex;
53978         var cm = this.cm, ds = this.ds;
53979         switch(item.id){
53980             case "asc":
53981                 ds.sort(cm.getDataIndex(index), "ASC");
53982                 break;
53983             case "desc":
53984                 ds.sort(cm.getDataIndex(index), "DESC");
53985                 break;
53986             case "lock":
53987                 var lc = cm.getLockedCount();
53988                 if(cm.getColumnCount(true) <= lc+1){
53989                     this.onDenyColumnLock();
53990                     return;
53991                 }
53992                 if(lc != index){
53993                     cm.setLocked(index, true, true);
53994                     cm.moveColumn(index, lc);
53995                     this.grid.fireEvent("columnmove", index, lc);
53996                 }else{
53997                     cm.setLocked(index, true);
53998                 }
53999             break;
54000             case "unlock":
54001                 var lc = cm.getLockedCount();
54002                 if((lc-1) != index){
54003                     cm.setLocked(index, false, true);
54004                     cm.moveColumn(index, lc-1);
54005                     this.grid.fireEvent("columnmove", index, lc-1);
54006                 }else{
54007                     cm.setLocked(index, false);
54008                 }
54009             break;
54010             case 'wider': // used to expand cols on touch..
54011             case 'narrow':
54012                 var cw = cm.getColumnWidth(index);
54013                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54014                 cw = Math.max(0, cw);
54015                 cw = Math.min(cw,4000);
54016                 cm.setColumnWidth(index, cw);
54017                 break;
54018                 
54019             default:
54020                 index = cm.getIndexById(item.id.substr(4));
54021                 if(index != -1){
54022                     if(item.checked && cm.getColumnCount(true) <= 1){
54023                         this.onDenyColumnHide();
54024                         return false;
54025                     }
54026                     cm.setHidden(index, item.checked);
54027                 }
54028         }
54029         return true;
54030     },
54031
54032     beforeColMenuShow : function(){
54033         var cm = this.cm,  colCount = cm.getColumnCount();
54034         this.colMenu.removeAll();
54035         for(var i = 0; i < colCount; i++){
54036             this.colMenu.add(new Roo.menu.CheckItem({
54037                 id: "col-"+cm.getColumnId(i),
54038                 text: cm.getColumnHeader(i),
54039                 checked: !cm.isHidden(i),
54040                 hideOnClick:false
54041             }));
54042         }
54043     },
54044
54045     handleHdCtx : function(g, index, e){
54046         e.stopEvent();
54047         var hd = this.getHeaderCell(index);
54048         this.hdCtxIndex = index;
54049         var ms = this.hmenu.items, cm = this.cm;
54050         ms.get("asc").setDisabled(!cm.isSortable(index));
54051         ms.get("desc").setDisabled(!cm.isSortable(index));
54052         if(this.grid.enableColLock !== false){
54053             ms.get("lock").setDisabled(cm.isLocked(index));
54054             ms.get("unlock").setDisabled(!cm.isLocked(index));
54055         }
54056         this.hmenu.show(hd, "tl-bl");
54057     },
54058
54059     handleHdOver : function(e){
54060         var hd = this.findHeaderCell(e.getTarget());
54061         if(hd && !this.headersDisabled){
54062             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54063                this.fly(hd).addClass("x-grid-hd-over");
54064             }
54065         }
54066     },
54067
54068     handleHdOut : function(e){
54069         var hd = this.findHeaderCell(e.getTarget());
54070         if(hd){
54071             this.fly(hd).removeClass("x-grid-hd-over");
54072         }
54073     },
54074
54075     handleSplitDblClick : function(e, t){
54076         var i = this.getCellIndex(t);
54077         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54078             this.autoSizeColumn(i, true);
54079             this.layout();
54080         }
54081     },
54082
54083     render : function(){
54084
54085         var cm = this.cm;
54086         var colCount = cm.getColumnCount();
54087
54088         if(this.grid.monitorWindowResize === true){
54089             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54090         }
54091         var header = this.renderHeaders();
54092         var body = this.templates.body.apply({rows:""});
54093         var html = this.templates.master.apply({
54094             lockedBody: body,
54095             body: body,
54096             lockedHeader: header[0],
54097             header: header[1]
54098         });
54099
54100         //this.updateColumns();
54101
54102         this.grid.getGridEl().dom.innerHTML = html;
54103
54104         this.initElements();
54105         
54106         // a kludge to fix the random scolling effect in webkit
54107         this.el.on("scroll", function() {
54108             this.el.dom.scrollTop=0; // hopefully not recursive..
54109         },this);
54110
54111         this.scroller.on("scroll", this.handleScroll, this);
54112         this.lockedBody.on("mousewheel", this.handleWheel, this);
54113         this.mainBody.on("mousewheel", this.handleWheel, this);
54114
54115         this.mainHd.on("mouseover", this.handleHdOver, this);
54116         this.mainHd.on("mouseout", this.handleHdOut, this);
54117         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54118                 {delegate: "."+this.splitClass});
54119
54120         this.lockedHd.on("mouseover", this.handleHdOver, this);
54121         this.lockedHd.on("mouseout", this.handleHdOut, this);
54122         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54123                 {delegate: "."+this.splitClass});
54124
54125         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54126             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54127         }
54128
54129         this.updateSplitters();
54130
54131         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54132             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54133             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54134         }
54135
54136         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54137             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54138             this.hmenu.add(
54139                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54140                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54141             );
54142             if(this.grid.enableColLock !== false){
54143                 this.hmenu.add('-',
54144                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54145                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54146                 );
54147             }
54148             if (Roo.isTouch) {
54149                  this.hmenu.add('-',
54150                     {id:"wider", text: this.columnsWiderText},
54151                     {id:"narrow", text: this.columnsNarrowText }
54152                 );
54153                 
54154                  
54155             }
54156             
54157             if(this.grid.enableColumnHide !== false){
54158
54159                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54160                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54161                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54162
54163                 this.hmenu.add('-',
54164                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54165                 );
54166             }
54167             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54168
54169             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54170         }
54171
54172         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54173             this.dd = new Roo.grid.GridDragZone(this.grid, {
54174                 ddGroup : this.grid.ddGroup || 'GridDD'
54175             });
54176             
54177         }
54178
54179         /*
54180         for(var i = 0; i < colCount; i++){
54181             if(cm.isHidden(i)){
54182                 this.hideColumn(i);
54183             }
54184             if(cm.config[i].align){
54185                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54186                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54187             }
54188         }*/
54189         
54190         this.updateHeaderSortState();
54191
54192         this.beforeInitialResize();
54193         this.layout(true);
54194
54195         // two part rendering gives faster view to the user
54196         this.renderPhase2.defer(1, this);
54197     },
54198
54199     renderPhase2 : function(){
54200         // render the rows now
54201         this.refresh();
54202         if(this.grid.autoSizeColumns){
54203             this.autoSizeColumns();
54204         }
54205     },
54206
54207     beforeInitialResize : function(){
54208
54209     },
54210
54211     onColumnSplitterMoved : function(i, w){
54212         this.userResized = true;
54213         var cm = this.grid.colModel;
54214         cm.setColumnWidth(i, w, true);
54215         var cid = cm.getColumnId(i);
54216         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54217         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54218         this.updateSplitters();
54219         this.layout();
54220         this.grid.fireEvent("columnresize", i, w);
54221     },
54222
54223     syncRowHeights : function(startIndex, endIndex){
54224         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54225             startIndex = startIndex || 0;
54226             var mrows = this.getBodyTable().rows;
54227             var lrows = this.getLockedTable().rows;
54228             var len = mrows.length-1;
54229             endIndex = Math.min(endIndex || len, len);
54230             for(var i = startIndex; i <= endIndex; i++){
54231                 var m = mrows[i], l = lrows[i];
54232                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54233                 m.style.height = l.style.height = h + "px";
54234             }
54235         }
54236     },
54237
54238     layout : function(initialRender, is2ndPass){
54239         var g = this.grid;
54240         var auto = g.autoHeight;
54241         var scrollOffset = 16;
54242         var c = g.getGridEl(), cm = this.cm,
54243                 expandCol = g.autoExpandColumn,
54244                 gv = this;
54245         //c.beginMeasure();
54246
54247         if(!c.dom.offsetWidth){ // display:none?
54248             if(initialRender){
54249                 this.lockedWrap.show();
54250                 this.mainWrap.show();
54251             }
54252             return;
54253         }
54254
54255         var hasLock = this.cm.isLocked(0);
54256
54257         var tbh = this.headerPanel.getHeight();
54258         var bbh = this.footerPanel.getHeight();
54259
54260         if(auto){
54261             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54262             var newHeight = ch + c.getBorderWidth("tb");
54263             if(g.maxHeight){
54264                 newHeight = Math.min(g.maxHeight, newHeight);
54265             }
54266             c.setHeight(newHeight);
54267         }
54268
54269         if(g.autoWidth){
54270             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54271         }
54272
54273         var s = this.scroller;
54274
54275         var csize = c.getSize(true);
54276
54277         this.el.setSize(csize.width, csize.height);
54278
54279         this.headerPanel.setWidth(csize.width);
54280         this.footerPanel.setWidth(csize.width);
54281
54282         var hdHeight = this.mainHd.getHeight();
54283         var vw = csize.width;
54284         var vh = csize.height - (tbh + bbh);
54285
54286         s.setSize(vw, vh);
54287
54288         var bt = this.getBodyTable();
54289         var ltWidth = hasLock ?
54290                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54291
54292         var scrollHeight = bt.offsetHeight;
54293         var scrollWidth = ltWidth + bt.offsetWidth;
54294         var vscroll = false, hscroll = false;
54295
54296         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54297
54298         var lw = this.lockedWrap, mw = this.mainWrap;
54299         var lb = this.lockedBody, mb = this.mainBody;
54300
54301         setTimeout(function(){
54302             var t = s.dom.offsetTop;
54303             var w = s.dom.clientWidth,
54304                 h = s.dom.clientHeight;
54305
54306             lw.setTop(t);
54307             lw.setSize(ltWidth, h);
54308
54309             mw.setLeftTop(ltWidth, t);
54310             mw.setSize(w-ltWidth, h);
54311
54312             lb.setHeight(h-hdHeight);
54313             mb.setHeight(h-hdHeight);
54314
54315             if(is2ndPass !== true && !gv.userResized && expandCol){
54316                 // high speed resize without full column calculation
54317                 
54318                 var ci = cm.getIndexById(expandCol);
54319                 if (ci < 0) {
54320                     ci = cm.findColumnIndex(expandCol);
54321                 }
54322                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54323                 var expandId = cm.getColumnId(ci);
54324                 var  tw = cm.getTotalWidth(false);
54325                 var currentWidth = cm.getColumnWidth(ci);
54326                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54327                 if(currentWidth != cw){
54328                     cm.setColumnWidth(ci, cw, true);
54329                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54330                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54331                     gv.updateSplitters();
54332                     gv.layout(false, true);
54333                 }
54334             }
54335
54336             if(initialRender){
54337                 lw.show();
54338                 mw.show();
54339             }
54340             //c.endMeasure();
54341         }, 10);
54342     },
54343
54344     onWindowResize : function(){
54345         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54346             return;
54347         }
54348         this.layout();
54349     },
54350
54351     appendFooter : function(parentEl){
54352         return null;
54353     },
54354
54355     sortAscText : "Sort Ascending",
54356     sortDescText : "Sort Descending",
54357     lockText : "Lock Column",
54358     unlockText : "Unlock Column",
54359     columnsText : "Columns",
54360  
54361     columnsWiderText : "Wider",
54362     columnsNarrowText : "Thinner"
54363 });
54364
54365
54366 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54367     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54368     this.proxy.el.addClass('x-grid3-col-dd');
54369 };
54370
54371 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54372     handleMouseDown : function(e){
54373
54374     },
54375
54376     callHandleMouseDown : function(e){
54377         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54378     }
54379 });
54380 /*
54381  * Based on:
54382  * Ext JS Library 1.1.1
54383  * Copyright(c) 2006-2007, Ext JS, LLC.
54384  *
54385  * Originally Released Under LGPL - original licence link has changed is not relivant.
54386  *
54387  * Fork - LGPL
54388  * <script type="text/javascript">
54389  */
54390  
54391 // private
54392 // This is a support class used internally by the Grid components
54393 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54394     this.grid = grid;
54395     this.view = grid.getView();
54396     this.proxy = this.view.resizeProxy;
54397     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54398         "gridSplitters" + this.grid.getGridEl().id, {
54399         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54400     });
54401     this.setHandleElId(Roo.id(hd));
54402     this.setOuterHandleElId(Roo.id(hd2));
54403     this.scroll = false;
54404 };
54405 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54406     fly: Roo.Element.fly,
54407
54408     b4StartDrag : function(x, y){
54409         this.view.headersDisabled = true;
54410         this.proxy.setHeight(this.view.mainWrap.getHeight());
54411         var w = this.cm.getColumnWidth(this.cellIndex);
54412         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54413         this.resetConstraints();
54414         this.setXConstraint(minw, 1000);
54415         this.setYConstraint(0, 0);
54416         this.minX = x - minw;
54417         this.maxX = x + 1000;
54418         this.startPos = x;
54419         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54420     },
54421
54422
54423     handleMouseDown : function(e){
54424         ev = Roo.EventObject.setEvent(e);
54425         var t = this.fly(ev.getTarget());
54426         if(t.hasClass("x-grid-split")){
54427             this.cellIndex = this.view.getCellIndex(t.dom);
54428             this.split = t.dom;
54429             this.cm = this.grid.colModel;
54430             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54431                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54432             }
54433         }
54434     },
54435
54436     endDrag : function(e){
54437         this.view.headersDisabled = false;
54438         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54439         var diff = endX - this.startPos;
54440         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54441     },
54442
54443     autoOffset : function(){
54444         this.setDelta(0,0);
54445     }
54446 });/*
54447  * Based on:
54448  * Ext JS Library 1.1.1
54449  * Copyright(c) 2006-2007, Ext JS, LLC.
54450  *
54451  * Originally Released Under LGPL - original licence link has changed is not relivant.
54452  *
54453  * Fork - LGPL
54454  * <script type="text/javascript">
54455  */
54456  
54457 // private
54458 // This is a support class used internally by the Grid components
54459 Roo.grid.GridDragZone = function(grid, config){
54460     this.view = grid.getView();
54461     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54462     if(this.view.lockedBody){
54463         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54464         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54465     }
54466     this.scroll = false;
54467     this.grid = grid;
54468     this.ddel = document.createElement('div');
54469     this.ddel.className = 'x-grid-dd-wrap';
54470 };
54471
54472 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54473     ddGroup : "GridDD",
54474
54475     getDragData : function(e){
54476         var t = Roo.lib.Event.getTarget(e);
54477         var rowIndex = this.view.findRowIndex(t);
54478         var sm = this.grid.selModel;
54479             
54480         //Roo.log(rowIndex);
54481         
54482         if (sm.getSelectedCell) {
54483             // cell selection..
54484             if (!sm.getSelectedCell()) {
54485                 return false;
54486             }
54487             if (rowIndex != sm.getSelectedCell()[0]) {
54488                 return false;
54489             }
54490         
54491         }
54492         
54493         if(rowIndex !== false){
54494             
54495             // if editorgrid.. 
54496             
54497             
54498             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54499                
54500             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54501               //  
54502             //}
54503             if (e.hasModifier()){
54504                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54505             }
54506             
54507             Roo.log("getDragData");
54508             
54509             return {
54510                 grid: this.grid,
54511                 ddel: this.ddel,
54512                 rowIndex: rowIndex,
54513                 selections:sm.getSelections ? sm.getSelections() : (
54514                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54515                 )
54516             };
54517         }
54518         return false;
54519     },
54520
54521     onInitDrag : function(e){
54522         var data = this.dragData;
54523         this.ddel.innerHTML = this.grid.getDragDropText();
54524         this.proxy.update(this.ddel);
54525         // fire start drag?
54526     },
54527
54528     afterRepair : function(){
54529         this.dragging = false;
54530     },
54531
54532     getRepairXY : function(e, data){
54533         return false;
54534     },
54535
54536     onEndDrag : function(data, e){
54537         // fire end drag?
54538     },
54539
54540     onValidDrop : function(dd, e, id){
54541         // fire drag drop?
54542         this.hideProxy();
54543     },
54544
54545     beforeInvalidDrop : function(e, id){
54546
54547     }
54548 });/*
54549  * Based on:
54550  * Ext JS Library 1.1.1
54551  * Copyright(c) 2006-2007, Ext JS, LLC.
54552  *
54553  * Originally Released Under LGPL - original licence link has changed is not relivant.
54554  *
54555  * Fork - LGPL
54556  * <script type="text/javascript">
54557  */
54558  
54559
54560 /**
54561  * @class Roo.grid.ColumnModel
54562  * @extends Roo.util.Observable
54563  * This is the default implementation of a ColumnModel used by the Grid. It defines
54564  * the columns in the grid.
54565  * <br>Usage:<br>
54566  <pre><code>
54567  var colModel = new Roo.grid.ColumnModel([
54568         {header: "Ticker", width: 60, sortable: true, locked: true},
54569         {header: "Company Name", width: 150, sortable: true},
54570         {header: "Market Cap.", width: 100, sortable: true},
54571         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54572         {header: "Employees", width: 100, sortable: true, resizable: false}
54573  ]);
54574  </code></pre>
54575  * <p>
54576  
54577  * The config options listed for this class are options which may appear in each
54578  * individual column definition.
54579  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54580  * @constructor
54581  * @param {Object} config An Array of column config objects. See this class's
54582  * config objects for details.
54583 */
54584 Roo.grid.ColumnModel = function(config){
54585         /**
54586      * The config passed into the constructor
54587      */
54588     this.config = config;
54589     this.lookup = {};
54590
54591     // if no id, create one
54592     // if the column does not have a dataIndex mapping,
54593     // map it to the order it is in the config
54594     for(var i = 0, len = config.length; i < len; i++){
54595         var c = config[i];
54596         if(typeof c.dataIndex == "undefined"){
54597             c.dataIndex = i;
54598         }
54599         if(typeof c.renderer == "string"){
54600             c.renderer = Roo.util.Format[c.renderer];
54601         }
54602         if(typeof c.id == "undefined"){
54603             c.id = Roo.id();
54604         }
54605         if(c.editor && c.editor.xtype){
54606             c.editor  = Roo.factory(c.editor, Roo.grid);
54607         }
54608         if(c.editor && c.editor.isFormField){
54609             c.editor = new Roo.grid.GridEditor(c.editor);
54610         }
54611         this.lookup[c.id] = c;
54612     }
54613
54614     /**
54615      * The width of columns which have no width specified (defaults to 100)
54616      * @type Number
54617      */
54618     this.defaultWidth = 100;
54619
54620     /**
54621      * Default sortable of columns which have no sortable specified (defaults to false)
54622      * @type Boolean
54623      */
54624     this.defaultSortable = false;
54625
54626     this.addEvents({
54627         /**
54628              * @event widthchange
54629              * Fires when the width of a column changes.
54630              * @param {ColumnModel} this
54631              * @param {Number} columnIndex The column index
54632              * @param {Number} newWidth The new width
54633              */
54634             "widthchange": true,
54635         /**
54636              * @event headerchange
54637              * Fires when the text of a header changes.
54638              * @param {ColumnModel} this
54639              * @param {Number} columnIndex The column index
54640              * @param {Number} newText The new header text
54641              */
54642             "headerchange": true,
54643         /**
54644              * @event hiddenchange
54645              * Fires when a column is hidden or "unhidden".
54646              * @param {ColumnModel} this
54647              * @param {Number} columnIndex The column index
54648              * @param {Boolean} hidden true if hidden, false otherwise
54649              */
54650             "hiddenchange": true,
54651             /**
54652          * @event columnmoved
54653          * Fires when a column is moved.
54654          * @param {ColumnModel} this
54655          * @param {Number} oldIndex
54656          * @param {Number} newIndex
54657          */
54658         "columnmoved" : true,
54659         /**
54660          * @event columlockchange
54661          * Fires when a column's locked state is changed
54662          * @param {ColumnModel} this
54663          * @param {Number} colIndex
54664          * @param {Boolean} locked true if locked
54665          */
54666         "columnlockchange" : true
54667     });
54668     Roo.grid.ColumnModel.superclass.constructor.call(this);
54669 };
54670 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54671     /**
54672      * @cfg {String} header The header text to display in the Grid view.
54673      */
54674     /**
54675      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54676      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54677      * specified, the column's index is used as an index into the Record's data Array.
54678      */
54679     /**
54680      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54681      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54682      */
54683     /**
54684      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54685      * Defaults to the value of the {@link #defaultSortable} property.
54686      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54687      */
54688     /**
54689      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54690      */
54691     /**
54692      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54693      */
54694     /**
54695      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54696      */
54697     /**
54698      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54699      */
54700     /**
54701      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54702      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54703      * default renderer uses the raw data value.
54704      */
54705        /**
54706      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54707      */
54708     /**
54709      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54710      */
54711
54712     /**
54713      * Returns the id of the column at the specified index.
54714      * @param {Number} index The column index
54715      * @return {String} the id
54716      */
54717     getColumnId : function(index){
54718         return this.config[index].id;
54719     },
54720
54721     /**
54722      * Returns the column for a specified id.
54723      * @param {String} id The column id
54724      * @return {Object} the column
54725      */
54726     getColumnById : function(id){
54727         return this.lookup[id];
54728     },
54729
54730     
54731     /**
54732      * Returns the column for a specified dataIndex.
54733      * @param {String} dataIndex The column dataIndex
54734      * @return {Object|Boolean} the column or false if not found
54735      */
54736     getColumnByDataIndex: function(dataIndex){
54737         var index = this.findColumnIndex(dataIndex);
54738         return index > -1 ? this.config[index] : false;
54739     },
54740     
54741     /**
54742      * Returns the index for a specified column id.
54743      * @param {String} id The column id
54744      * @return {Number} the index, or -1 if not found
54745      */
54746     getIndexById : function(id){
54747         for(var i = 0, len = this.config.length; i < len; i++){
54748             if(this.config[i].id == id){
54749                 return i;
54750             }
54751         }
54752         return -1;
54753     },
54754     
54755     /**
54756      * Returns the index for a specified column dataIndex.
54757      * @param {String} dataIndex The column dataIndex
54758      * @return {Number} the index, or -1 if not found
54759      */
54760     
54761     findColumnIndex : function(dataIndex){
54762         for(var i = 0, len = this.config.length; i < len; i++){
54763             if(this.config[i].dataIndex == dataIndex){
54764                 return i;
54765             }
54766         }
54767         return -1;
54768     },
54769     
54770     
54771     moveColumn : function(oldIndex, newIndex){
54772         var c = this.config[oldIndex];
54773         this.config.splice(oldIndex, 1);
54774         this.config.splice(newIndex, 0, c);
54775         this.dataMap = null;
54776         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54777     },
54778
54779     isLocked : function(colIndex){
54780         return this.config[colIndex].locked === true;
54781     },
54782
54783     setLocked : function(colIndex, value, suppressEvent){
54784         if(this.isLocked(colIndex) == value){
54785             return;
54786         }
54787         this.config[colIndex].locked = value;
54788         if(!suppressEvent){
54789             this.fireEvent("columnlockchange", this, colIndex, value);
54790         }
54791     },
54792
54793     getTotalLockedWidth : function(){
54794         var totalWidth = 0;
54795         for(var i = 0; i < this.config.length; i++){
54796             if(this.isLocked(i) && !this.isHidden(i)){
54797                 this.totalWidth += this.getColumnWidth(i);
54798             }
54799         }
54800         return totalWidth;
54801     },
54802
54803     getLockedCount : function(){
54804         for(var i = 0, len = this.config.length; i < len; i++){
54805             if(!this.isLocked(i)){
54806                 return i;
54807             }
54808         }
54809     },
54810
54811     /**
54812      * Returns the number of columns.
54813      * @return {Number}
54814      */
54815     getColumnCount : function(visibleOnly){
54816         if(visibleOnly === true){
54817             var c = 0;
54818             for(var i = 0, len = this.config.length; i < len; i++){
54819                 if(!this.isHidden(i)){
54820                     c++;
54821                 }
54822             }
54823             return c;
54824         }
54825         return this.config.length;
54826     },
54827
54828     /**
54829      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54830      * @param {Function} fn
54831      * @param {Object} scope (optional)
54832      * @return {Array} result
54833      */
54834     getColumnsBy : function(fn, scope){
54835         var r = [];
54836         for(var i = 0, len = this.config.length; i < len; i++){
54837             var c = this.config[i];
54838             if(fn.call(scope||this, c, i) === true){
54839                 r[r.length] = c;
54840             }
54841         }
54842         return r;
54843     },
54844
54845     /**
54846      * Returns true if the specified column is sortable.
54847      * @param {Number} col The column index
54848      * @return {Boolean}
54849      */
54850     isSortable : function(col){
54851         if(typeof this.config[col].sortable == "undefined"){
54852             return this.defaultSortable;
54853         }
54854         return this.config[col].sortable;
54855     },
54856
54857     /**
54858      * Returns the rendering (formatting) function defined for the column.
54859      * @param {Number} col The column index.
54860      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54861      */
54862     getRenderer : function(col){
54863         if(!this.config[col].renderer){
54864             return Roo.grid.ColumnModel.defaultRenderer;
54865         }
54866         return this.config[col].renderer;
54867     },
54868
54869     /**
54870      * Sets the rendering (formatting) function for a column.
54871      * @param {Number} col The column index
54872      * @param {Function} fn The function to use to process the cell's raw data
54873      * to return HTML markup for the grid view. The render function is called with
54874      * the following parameters:<ul>
54875      * <li>Data value.</li>
54876      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54877      * <li>css A CSS style string to apply to the table cell.</li>
54878      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54879      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54880      * <li>Row index</li>
54881      * <li>Column index</li>
54882      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54883      */
54884     setRenderer : function(col, fn){
54885         this.config[col].renderer = fn;
54886     },
54887
54888     /**
54889      * Returns the width for the specified column.
54890      * @param {Number} col The column index
54891      * @return {Number}
54892      */
54893     getColumnWidth : function(col){
54894         return this.config[col].width * 1 || this.defaultWidth;
54895     },
54896
54897     /**
54898      * Sets the width for a column.
54899      * @param {Number} col The column index
54900      * @param {Number} width The new width
54901      */
54902     setColumnWidth : function(col, width, suppressEvent){
54903         this.config[col].width = width;
54904         this.totalWidth = null;
54905         if(!suppressEvent){
54906              this.fireEvent("widthchange", this, col, width);
54907         }
54908     },
54909
54910     /**
54911      * Returns the total width of all columns.
54912      * @param {Boolean} includeHidden True to include hidden column widths
54913      * @return {Number}
54914      */
54915     getTotalWidth : function(includeHidden){
54916         if(!this.totalWidth){
54917             this.totalWidth = 0;
54918             for(var i = 0, len = this.config.length; i < len; i++){
54919                 if(includeHidden || !this.isHidden(i)){
54920                     this.totalWidth += this.getColumnWidth(i);
54921                 }
54922             }
54923         }
54924         return this.totalWidth;
54925     },
54926
54927     /**
54928      * Returns the header for the specified column.
54929      * @param {Number} col The column index
54930      * @return {String}
54931      */
54932     getColumnHeader : function(col){
54933         return this.config[col].header;
54934     },
54935
54936     /**
54937      * Sets the header for a column.
54938      * @param {Number} col The column index
54939      * @param {String} header The new header
54940      */
54941     setColumnHeader : function(col, header){
54942         this.config[col].header = header;
54943         this.fireEvent("headerchange", this, col, header);
54944     },
54945
54946     /**
54947      * Returns the tooltip for the specified column.
54948      * @param {Number} col The column index
54949      * @return {String}
54950      */
54951     getColumnTooltip : function(col){
54952             return this.config[col].tooltip;
54953     },
54954     /**
54955      * Sets the tooltip for a column.
54956      * @param {Number} col The column index
54957      * @param {String} tooltip The new tooltip
54958      */
54959     setColumnTooltip : function(col, tooltip){
54960             this.config[col].tooltip = tooltip;
54961     },
54962
54963     /**
54964      * Returns the dataIndex for the specified column.
54965      * @param {Number} col The column index
54966      * @return {Number}
54967      */
54968     getDataIndex : function(col){
54969         return this.config[col].dataIndex;
54970     },
54971
54972     /**
54973      * Sets the dataIndex for a column.
54974      * @param {Number} col The column index
54975      * @param {Number} dataIndex The new dataIndex
54976      */
54977     setDataIndex : function(col, dataIndex){
54978         this.config[col].dataIndex = dataIndex;
54979     },
54980
54981     
54982     
54983     /**
54984      * Returns true if the cell is editable.
54985      * @param {Number} colIndex The column index
54986      * @param {Number} rowIndex The row index
54987      * @return {Boolean}
54988      */
54989     isCellEditable : function(colIndex, rowIndex){
54990         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54991     },
54992
54993     /**
54994      * Returns the editor defined for the cell/column.
54995      * return false or null to disable editing.
54996      * @param {Number} colIndex The column index
54997      * @param {Number} rowIndex The row index
54998      * @return {Object}
54999      */
55000     getCellEditor : function(colIndex, rowIndex){
55001         return this.config[colIndex].editor;
55002     },
55003
55004     /**
55005      * Sets if a column is editable.
55006      * @param {Number} col The column index
55007      * @param {Boolean} editable True if the column is editable
55008      */
55009     setEditable : function(col, editable){
55010         this.config[col].editable = editable;
55011     },
55012
55013
55014     /**
55015      * Returns true if the column is hidden.
55016      * @param {Number} colIndex The column index
55017      * @return {Boolean}
55018      */
55019     isHidden : function(colIndex){
55020         return this.config[colIndex].hidden;
55021     },
55022
55023
55024     /**
55025      * Returns true if the column width cannot be changed
55026      */
55027     isFixed : function(colIndex){
55028         return this.config[colIndex].fixed;
55029     },
55030
55031     /**
55032      * Returns true if the column can be resized
55033      * @return {Boolean}
55034      */
55035     isResizable : function(colIndex){
55036         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55037     },
55038     /**
55039      * Sets if a column is hidden.
55040      * @param {Number} colIndex The column index
55041      * @param {Boolean} hidden True if the column is hidden
55042      */
55043     setHidden : function(colIndex, hidden){
55044         this.config[colIndex].hidden = hidden;
55045         this.totalWidth = null;
55046         this.fireEvent("hiddenchange", this, colIndex, hidden);
55047     },
55048
55049     /**
55050      * Sets the editor for a column.
55051      * @param {Number} col The column index
55052      * @param {Object} editor The editor object
55053      */
55054     setEditor : function(col, editor){
55055         this.config[col].editor = editor;
55056     }
55057 });
55058
55059 Roo.grid.ColumnModel.defaultRenderer = function(value){
55060         if(typeof value == "string" && value.length < 1){
55061             return "&#160;";
55062         }
55063         return value;
55064 };
55065
55066 // Alias for backwards compatibility
55067 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55068 /*
55069  * Based on:
55070  * Ext JS Library 1.1.1
55071  * Copyright(c) 2006-2007, Ext JS, LLC.
55072  *
55073  * Originally Released Under LGPL - original licence link has changed is not relivant.
55074  *
55075  * Fork - LGPL
55076  * <script type="text/javascript">
55077  */
55078
55079 /**
55080  * @class Roo.grid.AbstractSelectionModel
55081  * @extends Roo.util.Observable
55082  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55083  * implemented by descendant classes.  This class should not be directly instantiated.
55084  * @constructor
55085  */
55086 Roo.grid.AbstractSelectionModel = function(){
55087     this.locked = false;
55088     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55089 };
55090
55091 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55092     /** @ignore Called by the grid automatically. Do not call directly. */
55093     init : function(grid){
55094         this.grid = grid;
55095         this.initEvents();
55096     },
55097
55098     /**
55099      * Locks the selections.
55100      */
55101     lock : function(){
55102         this.locked = true;
55103     },
55104
55105     /**
55106      * Unlocks the selections.
55107      */
55108     unlock : function(){
55109         this.locked = false;
55110     },
55111
55112     /**
55113      * Returns true if the selections are locked.
55114      * @return {Boolean}
55115      */
55116     isLocked : function(){
55117         return this.locked;
55118     }
55119 });/*
55120  * Based on:
55121  * Ext JS Library 1.1.1
55122  * Copyright(c) 2006-2007, Ext JS, LLC.
55123  *
55124  * Originally Released Under LGPL - original licence link has changed is not relivant.
55125  *
55126  * Fork - LGPL
55127  * <script type="text/javascript">
55128  */
55129 /**
55130  * @extends Roo.grid.AbstractSelectionModel
55131  * @class Roo.grid.RowSelectionModel
55132  * The default SelectionModel used by {@link Roo.grid.Grid}.
55133  * It supports multiple selections and keyboard selection/navigation. 
55134  * @constructor
55135  * @param {Object} config
55136  */
55137 Roo.grid.RowSelectionModel = function(config){
55138     Roo.apply(this, config);
55139     this.selections = new Roo.util.MixedCollection(false, function(o){
55140         return o.id;
55141     });
55142
55143     this.last = false;
55144     this.lastActive = false;
55145
55146     this.addEvents({
55147         /**
55148              * @event selectionchange
55149              * Fires when the selection changes
55150              * @param {SelectionModel} this
55151              */
55152             "selectionchange" : true,
55153         /**
55154              * @event afterselectionchange
55155              * Fires after the selection changes (eg. by key press or clicking)
55156              * @param {SelectionModel} this
55157              */
55158             "afterselectionchange" : true,
55159         /**
55160              * @event beforerowselect
55161              * Fires when a row is selected being selected, return false to cancel.
55162              * @param {SelectionModel} this
55163              * @param {Number} rowIndex The selected index
55164              * @param {Boolean} keepExisting False if other selections will be cleared
55165              */
55166             "beforerowselect" : true,
55167         /**
55168              * @event rowselect
55169              * Fires when a row is selected.
55170              * @param {SelectionModel} this
55171              * @param {Number} rowIndex The selected index
55172              * @param {Roo.data.Record} r The record
55173              */
55174             "rowselect" : true,
55175         /**
55176              * @event rowdeselect
55177              * Fires when a row is deselected.
55178              * @param {SelectionModel} this
55179              * @param {Number} rowIndex The selected index
55180              */
55181         "rowdeselect" : true
55182     });
55183     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55184     this.locked = false;
55185 };
55186
55187 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55188     /**
55189      * @cfg {Boolean} singleSelect
55190      * True to allow selection of only one row at a time (defaults to false)
55191      */
55192     singleSelect : false,
55193
55194     // private
55195     initEvents : function(){
55196
55197         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55198             this.grid.on("mousedown", this.handleMouseDown, this);
55199         }else{ // allow click to work like normal
55200             this.grid.on("rowclick", this.handleDragableRowClick, this);
55201         }
55202
55203         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55204             "up" : function(e){
55205                 if(!e.shiftKey){
55206                     this.selectPrevious(e.shiftKey);
55207                 }else if(this.last !== false && this.lastActive !== false){
55208                     var last = this.last;
55209                     this.selectRange(this.last,  this.lastActive-1);
55210                     this.grid.getView().focusRow(this.lastActive);
55211                     if(last !== false){
55212                         this.last = last;
55213                     }
55214                 }else{
55215                     this.selectFirstRow();
55216                 }
55217                 this.fireEvent("afterselectionchange", this);
55218             },
55219             "down" : function(e){
55220                 if(!e.shiftKey){
55221                     this.selectNext(e.shiftKey);
55222                 }else if(this.last !== false && this.lastActive !== false){
55223                     var last = this.last;
55224                     this.selectRange(this.last,  this.lastActive+1);
55225                     this.grid.getView().focusRow(this.lastActive);
55226                     if(last !== false){
55227                         this.last = last;
55228                     }
55229                 }else{
55230                     this.selectFirstRow();
55231                 }
55232                 this.fireEvent("afterselectionchange", this);
55233             },
55234             scope: this
55235         });
55236
55237         var view = this.grid.view;
55238         view.on("refresh", this.onRefresh, this);
55239         view.on("rowupdated", this.onRowUpdated, this);
55240         view.on("rowremoved", this.onRemove, this);
55241     },
55242
55243     // private
55244     onRefresh : function(){
55245         var ds = this.grid.dataSource, i, v = this.grid.view;
55246         var s = this.selections;
55247         s.each(function(r){
55248             if((i = ds.indexOfId(r.id)) != -1){
55249                 v.onRowSelect(i);
55250             }else{
55251                 s.remove(r);
55252             }
55253         });
55254     },
55255
55256     // private
55257     onRemove : function(v, index, r){
55258         this.selections.remove(r);
55259     },
55260
55261     // private
55262     onRowUpdated : function(v, index, r){
55263         if(this.isSelected(r)){
55264             v.onRowSelect(index);
55265         }
55266     },
55267
55268     /**
55269      * Select records.
55270      * @param {Array} records The records to select
55271      * @param {Boolean} keepExisting (optional) True to keep existing selections
55272      */
55273     selectRecords : function(records, keepExisting){
55274         if(!keepExisting){
55275             this.clearSelections();
55276         }
55277         var ds = this.grid.dataSource;
55278         for(var i = 0, len = records.length; i < len; i++){
55279             this.selectRow(ds.indexOf(records[i]), true);
55280         }
55281     },
55282
55283     /**
55284      * Gets the number of selected rows.
55285      * @return {Number}
55286      */
55287     getCount : function(){
55288         return this.selections.length;
55289     },
55290
55291     /**
55292      * Selects the first row in the grid.
55293      */
55294     selectFirstRow : function(){
55295         this.selectRow(0);
55296     },
55297
55298     /**
55299      * Select the last row.
55300      * @param {Boolean} keepExisting (optional) True to keep existing selections
55301      */
55302     selectLastRow : function(keepExisting){
55303         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55304     },
55305
55306     /**
55307      * Selects the row immediately following the last selected row.
55308      * @param {Boolean} keepExisting (optional) True to keep existing selections
55309      */
55310     selectNext : function(keepExisting){
55311         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55312             this.selectRow(this.last+1, keepExisting);
55313             this.grid.getView().focusRow(this.last);
55314         }
55315     },
55316
55317     /**
55318      * Selects the row that precedes the last selected row.
55319      * @param {Boolean} keepExisting (optional) True to keep existing selections
55320      */
55321     selectPrevious : function(keepExisting){
55322         if(this.last){
55323             this.selectRow(this.last-1, keepExisting);
55324             this.grid.getView().focusRow(this.last);
55325         }
55326     },
55327
55328     /**
55329      * Returns the selected records
55330      * @return {Array} Array of selected records
55331      */
55332     getSelections : function(){
55333         return [].concat(this.selections.items);
55334     },
55335
55336     /**
55337      * Returns the first selected record.
55338      * @return {Record}
55339      */
55340     getSelected : function(){
55341         return this.selections.itemAt(0);
55342     },
55343
55344
55345     /**
55346      * Clears all selections.
55347      */
55348     clearSelections : function(fast){
55349         if(this.locked) return;
55350         if(fast !== true){
55351             var ds = this.grid.dataSource;
55352             var s = this.selections;
55353             s.each(function(r){
55354                 this.deselectRow(ds.indexOfId(r.id));
55355             }, this);
55356             s.clear();
55357         }else{
55358             this.selections.clear();
55359         }
55360         this.last = false;
55361     },
55362
55363
55364     /**
55365      * Selects all rows.
55366      */
55367     selectAll : function(){
55368         if(this.locked) return;
55369         this.selections.clear();
55370         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55371             this.selectRow(i, true);
55372         }
55373     },
55374
55375     /**
55376      * Returns True if there is a selection.
55377      * @return {Boolean}
55378      */
55379     hasSelection : function(){
55380         return this.selections.length > 0;
55381     },
55382
55383     /**
55384      * Returns True if the specified row is selected.
55385      * @param {Number/Record} record The record or index of the record to check
55386      * @return {Boolean}
55387      */
55388     isSelected : function(index){
55389         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55390         return (r && this.selections.key(r.id) ? true : false);
55391     },
55392
55393     /**
55394      * Returns True if the specified record id is selected.
55395      * @param {String} id The id of record to check
55396      * @return {Boolean}
55397      */
55398     isIdSelected : function(id){
55399         return (this.selections.key(id) ? true : false);
55400     },
55401
55402     // private
55403     handleMouseDown : function(e, t){
55404         var view = this.grid.getView(), rowIndex;
55405         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55406             return;
55407         };
55408         if(e.shiftKey && this.last !== false){
55409             var last = this.last;
55410             this.selectRange(last, rowIndex, e.ctrlKey);
55411             this.last = last; // reset the last
55412             view.focusRow(rowIndex);
55413         }else{
55414             var isSelected = this.isSelected(rowIndex);
55415             if(e.button !== 0 && isSelected){
55416                 view.focusRow(rowIndex);
55417             }else if(e.ctrlKey && isSelected){
55418                 this.deselectRow(rowIndex);
55419             }else if(!isSelected){
55420                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55421                 view.focusRow(rowIndex);
55422             }
55423         }
55424         this.fireEvent("afterselectionchange", this);
55425     },
55426     // private
55427     handleDragableRowClick :  function(grid, rowIndex, e) 
55428     {
55429         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55430             this.selectRow(rowIndex, false);
55431             grid.view.focusRow(rowIndex);
55432              this.fireEvent("afterselectionchange", this);
55433         }
55434     },
55435     
55436     /**
55437      * Selects multiple rows.
55438      * @param {Array} rows Array of the indexes of the row to select
55439      * @param {Boolean} keepExisting (optional) True to keep existing selections
55440      */
55441     selectRows : function(rows, keepExisting){
55442         if(!keepExisting){
55443             this.clearSelections();
55444         }
55445         for(var i = 0, len = rows.length; i < len; i++){
55446             this.selectRow(rows[i], true);
55447         }
55448     },
55449
55450     /**
55451      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55452      * @param {Number} startRow The index of the first row in the range
55453      * @param {Number} endRow The index of the last row in the range
55454      * @param {Boolean} keepExisting (optional) True to retain existing selections
55455      */
55456     selectRange : function(startRow, endRow, keepExisting){
55457         if(this.locked) return;
55458         if(!keepExisting){
55459             this.clearSelections();
55460         }
55461         if(startRow <= endRow){
55462             for(var i = startRow; i <= endRow; i++){
55463                 this.selectRow(i, true);
55464             }
55465         }else{
55466             for(var i = startRow; i >= endRow; i--){
55467                 this.selectRow(i, true);
55468             }
55469         }
55470     },
55471
55472     /**
55473      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55474      * @param {Number} startRow The index of the first row in the range
55475      * @param {Number} endRow The index of the last row in the range
55476      */
55477     deselectRange : function(startRow, endRow, preventViewNotify){
55478         if(this.locked) return;
55479         for(var i = startRow; i <= endRow; i++){
55480             this.deselectRow(i, preventViewNotify);
55481         }
55482     },
55483
55484     /**
55485      * Selects a row.
55486      * @param {Number} row The index of the row to select
55487      * @param {Boolean} keepExisting (optional) True to keep existing selections
55488      */
55489     selectRow : function(index, keepExisting, preventViewNotify){
55490         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55491         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55492             if(!keepExisting || this.singleSelect){
55493                 this.clearSelections();
55494             }
55495             var r = this.grid.dataSource.getAt(index);
55496             this.selections.add(r);
55497             this.last = this.lastActive = index;
55498             if(!preventViewNotify){
55499                 this.grid.getView().onRowSelect(index);
55500             }
55501             this.fireEvent("rowselect", this, index, r);
55502             this.fireEvent("selectionchange", this);
55503         }
55504     },
55505
55506     /**
55507      * Deselects a row.
55508      * @param {Number} row The index of the row to deselect
55509      */
55510     deselectRow : function(index, preventViewNotify){
55511         if(this.locked) return;
55512         if(this.last == index){
55513             this.last = false;
55514         }
55515         if(this.lastActive == index){
55516             this.lastActive = false;
55517         }
55518         var r = this.grid.dataSource.getAt(index);
55519         this.selections.remove(r);
55520         if(!preventViewNotify){
55521             this.grid.getView().onRowDeselect(index);
55522         }
55523         this.fireEvent("rowdeselect", this, index);
55524         this.fireEvent("selectionchange", this);
55525     },
55526
55527     // private
55528     restoreLast : function(){
55529         if(this._last){
55530             this.last = this._last;
55531         }
55532     },
55533
55534     // private
55535     acceptsNav : function(row, col, cm){
55536         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55537     },
55538
55539     // private
55540     onEditorKey : function(field, e){
55541         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55542         if(k == e.TAB){
55543             e.stopEvent();
55544             ed.completeEdit();
55545             if(e.shiftKey){
55546                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55547             }else{
55548                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55549             }
55550         }else if(k == e.ENTER && !e.ctrlKey){
55551             e.stopEvent();
55552             ed.completeEdit();
55553             if(e.shiftKey){
55554                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55555             }else{
55556                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55557             }
55558         }else if(k == e.ESC){
55559             ed.cancelEdit();
55560         }
55561         if(newCell){
55562             g.startEditing(newCell[0], newCell[1]);
55563         }
55564     }
55565 });/*
55566  * Based on:
55567  * Ext JS Library 1.1.1
55568  * Copyright(c) 2006-2007, Ext JS, LLC.
55569  *
55570  * Originally Released Under LGPL - original licence link has changed is not relivant.
55571  *
55572  * Fork - LGPL
55573  * <script type="text/javascript">
55574  */
55575 /**
55576  * @class Roo.grid.CellSelectionModel
55577  * @extends Roo.grid.AbstractSelectionModel
55578  * This class provides the basic implementation for cell selection in a grid.
55579  * @constructor
55580  * @param {Object} config The object containing the configuration of this model.
55581  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55582  */
55583 Roo.grid.CellSelectionModel = function(config){
55584     Roo.apply(this, config);
55585
55586     this.selection = null;
55587
55588     this.addEvents({
55589         /**
55590              * @event beforerowselect
55591              * Fires before a cell is selected.
55592              * @param {SelectionModel} this
55593              * @param {Number} rowIndex The selected row index
55594              * @param {Number} colIndex The selected cell index
55595              */
55596             "beforecellselect" : true,
55597         /**
55598              * @event cellselect
55599              * Fires when a cell is selected.
55600              * @param {SelectionModel} this
55601              * @param {Number} rowIndex The selected row index
55602              * @param {Number} colIndex The selected cell index
55603              */
55604             "cellselect" : true,
55605         /**
55606              * @event selectionchange
55607              * Fires when the active selection changes.
55608              * @param {SelectionModel} this
55609              * @param {Object} selection null for no selection or an object (o) with two properties
55610                 <ul>
55611                 <li>o.record: the record object for the row the selection is in</li>
55612                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55613                 </ul>
55614              */
55615             "selectionchange" : true,
55616         /**
55617              * @event tabend
55618              * Fires when the tab (or enter) was pressed on the last editable cell
55619              * You can use this to trigger add new row.
55620              * @param {SelectionModel} this
55621              */
55622             "tabend" : true,
55623          /**
55624              * @event beforeeditnext
55625              * Fires before the next editable sell is made active
55626              * You can use this to skip to another cell or fire the tabend
55627              *    if you set cell to false
55628              * @param {Object} eventdata object : { cell : [ row, col ] } 
55629              */
55630             "beforeeditnext" : true
55631     });
55632     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55633 };
55634
55635 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55636     
55637     enter_is_tab: false,
55638
55639     /** @ignore */
55640     initEvents : function(){
55641         this.grid.on("mousedown", this.handleMouseDown, this);
55642         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55643         var view = this.grid.view;
55644         view.on("refresh", this.onViewChange, this);
55645         view.on("rowupdated", this.onRowUpdated, this);
55646         view.on("beforerowremoved", this.clearSelections, this);
55647         view.on("beforerowsinserted", this.clearSelections, this);
55648         if(this.grid.isEditor){
55649             this.grid.on("beforeedit", this.beforeEdit,  this);
55650         }
55651     },
55652
55653         //private
55654     beforeEdit : function(e){
55655         this.select(e.row, e.column, false, true, e.record);
55656     },
55657
55658         //private
55659     onRowUpdated : function(v, index, r){
55660         if(this.selection && this.selection.record == r){
55661             v.onCellSelect(index, this.selection.cell[1]);
55662         }
55663     },
55664
55665         //private
55666     onViewChange : function(){
55667         this.clearSelections(true);
55668     },
55669
55670         /**
55671          * Returns the currently selected cell,.
55672          * @return {Array} The selected cell (row, column) or null if none selected.
55673          */
55674     getSelectedCell : function(){
55675         return this.selection ? this.selection.cell : null;
55676     },
55677
55678     /**
55679      * Clears all selections.
55680      * @param {Boolean} true to prevent the gridview from being notified about the change.
55681      */
55682     clearSelections : function(preventNotify){
55683         var s = this.selection;
55684         if(s){
55685             if(preventNotify !== true){
55686                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55687             }
55688             this.selection = null;
55689             this.fireEvent("selectionchange", this, null);
55690         }
55691     },
55692
55693     /**
55694      * Returns true if there is a selection.
55695      * @return {Boolean}
55696      */
55697     hasSelection : function(){
55698         return this.selection ? true : false;
55699     },
55700
55701     /** @ignore */
55702     handleMouseDown : function(e, t){
55703         var v = this.grid.getView();
55704         if(this.isLocked()){
55705             return;
55706         };
55707         var row = v.findRowIndex(t);
55708         var cell = v.findCellIndex(t);
55709         if(row !== false && cell !== false){
55710             this.select(row, cell);
55711         }
55712     },
55713
55714     /**
55715      * Selects a cell.
55716      * @param {Number} rowIndex
55717      * @param {Number} collIndex
55718      */
55719     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55720         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55721             this.clearSelections();
55722             r = r || this.grid.dataSource.getAt(rowIndex);
55723             this.selection = {
55724                 record : r,
55725                 cell : [rowIndex, colIndex]
55726             };
55727             if(!preventViewNotify){
55728                 var v = this.grid.getView();
55729                 v.onCellSelect(rowIndex, colIndex);
55730                 if(preventFocus !== true){
55731                     v.focusCell(rowIndex, colIndex);
55732                 }
55733             }
55734             this.fireEvent("cellselect", this, rowIndex, colIndex);
55735             this.fireEvent("selectionchange", this, this.selection);
55736         }
55737     },
55738
55739         //private
55740     isSelectable : function(rowIndex, colIndex, cm){
55741         return !cm.isHidden(colIndex);
55742     },
55743
55744     /** @ignore */
55745     handleKeyDown : function(e){
55746         //Roo.log('Cell Sel Model handleKeyDown');
55747         if(!e.isNavKeyPress()){
55748             return;
55749         }
55750         var g = this.grid, s = this.selection;
55751         if(!s){
55752             e.stopEvent();
55753             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55754             if(cell){
55755                 this.select(cell[0], cell[1]);
55756             }
55757             return;
55758         }
55759         var sm = this;
55760         var walk = function(row, col, step){
55761             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55762         };
55763         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55764         var newCell;
55765
55766       
55767
55768         switch(k){
55769             case e.TAB:
55770                 // handled by onEditorKey
55771                 if (g.isEditor && g.editing) {
55772                     return;
55773                 }
55774                 if(e.shiftKey) {
55775                     newCell = walk(r, c-1, -1);
55776                 } else {
55777                     newCell = walk(r, c+1, 1);
55778                 }
55779                 break;
55780             
55781             case e.DOWN:
55782                newCell = walk(r+1, c, 1);
55783                 break;
55784             
55785             case e.UP:
55786                 newCell = walk(r-1, c, -1);
55787                 break;
55788             
55789             case e.RIGHT:
55790                 newCell = walk(r, c+1, 1);
55791                 break;
55792             
55793             case e.LEFT:
55794                 newCell = walk(r, c-1, -1);
55795                 break;
55796             
55797             case e.ENTER:
55798                 
55799                 if(g.isEditor && !g.editing){
55800                    g.startEditing(r, c);
55801                    e.stopEvent();
55802                    return;
55803                 }
55804                 
55805                 
55806              break;
55807         };
55808         if(newCell){
55809             this.select(newCell[0], newCell[1]);
55810             e.stopEvent();
55811             
55812         }
55813     },
55814
55815     acceptsNav : function(row, col, cm){
55816         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55817     },
55818     /**
55819      * Selects a cell.
55820      * @param {Number} field (not used) - as it's normally used as a listener
55821      * @param {Number} e - event - fake it by using
55822      *
55823      * var e = Roo.EventObjectImpl.prototype;
55824      * e.keyCode = e.TAB
55825      *
55826      * 
55827      */
55828     onEditorKey : function(field, e){
55829         
55830         var k = e.getKey(),
55831             newCell,
55832             g = this.grid,
55833             ed = g.activeEditor,
55834             forward = false;
55835         ///Roo.log('onEditorKey' + k);
55836         
55837         
55838         if (this.enter_is_tab && k == e.ENTER) {
55839             k = e.TAB;
55840         }
55841         
55842         if(k == e.TAB){
55843             if(e.shiftKey){
55844                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55845             }else{
55846                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55847                 forward = true;
55848             }
55849             
55850             e.stopEvent();
55851             
55852         } else if(k == e.ENTER &&  !e.ctrlKey){
55853             ed.completeEdit();
55854             e.stopEvent();
55855             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55856         
55857                 } else if(k == e.ESC){
55858             ed.cancelEdit();
55859         }
55860                 
55861         if (newCell) {
55862             var ecall = { cell : newCell, forward : forward };
55863             this.fireEvent('beforeeditnext', ecall );
55864             newCell = ecall.cell;
55865                         forward = ecall.forward;
55866         }
55867                 
55868         if(newCell){
55869             //Roo.log('next cell after edit');
55870             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55871         } else if (forward) {
55872             // tabbed past last
55873             this.fireEvent.defer(100, this, ['tabend',this]);
55874         }
55875     }
55876 });/*
55877  * Based on:
55878  * Ext JS Library 1.1.1
55879  * Copyright(c) 2006-2007, Ext JS, LLC.
55880  *
55881  * Originally Released Under LGPL - original licence link has changed is not relivant.
55882  *
55883  * Fork - LGPL
55884  * <script type="text/javascript">
55885  */
55886  
55887 /**
55888  * @class Roo.grid.EditorGrid
55889  * @extends Roo.grid.Grid
55890  * Class for creating and editable grid.
55891  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55892  * The container MUST have some type of size defined for the grid to fill. The container will be 
55893  * automatically set to position relative if it isn't already.
55894  * @param {Object} dataSource The data model to bind to
55895  * @param {Object} colModel The column model with info about this grid's columns
55896  */
55897 Roo.grid.EditorGrid = function(container, config){
55898     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55899     this.getGridEl().addClass("xedit-grid");
55900
55901     if(!this.selModel){
55902         this.selModel = new Roo.grid.CellSelectionModel();
55903     }
55904
55905     this.activeEditor = null;
55906
55907         this.addEvents({
55908             /**
55909              * @event beforeedit
55910              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55911              * <ul style="padding:5px;padding-left:16px;">
55912              * <li>grid - This grid</li>
55913              * <li>record - The record being edited</li>
55914              * <li>field - The field name being edited</li>
55915              * <li>value - The value for the field being edited.</li>
55916              * <li>row - The grid row index</li>
55917              * <li>column - The grid column index</li>
55918              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55919              * </ul>
55920              * @param {Object} e An edit event (see above for description)
55921              */
55922             "beforeedit" : true,
55923             /**
55924              * @event afteredit
55925              * Fires after a cell is edited. <br />
55926              * <ul style="padding:5px;padding-left:16px;">
55927              * <li>grid - This grid</li>
55928              * <li>record - The record being edited</li>
55929              * <li>field - The field name being edited</li>
55930              * <li>value - The value being set</li>
55931              * <li>originalValue - The original value for the field, before the edit.</li>
55932              * <li>row - The grid row index</li>
55933              * <li>column - The grid column index</li>
55934              * </ul>
55935              * @param {Object} e An edit event (see above for description)
55936              */
55937             "afteredit" : true,
55938             /**
55939              * @event validateedit
55940              * Fires after a cell is edited, but before the value is set in the record. 
55941          * You can use this to modify the value being set in the field, Return false
55942              * to cancel the change. The edit event object has the following properties <br />
55943              * <ul style="padding:5px;padding-left:16px;">
55944          * <li>editor - This editor</li>
55945              * <li>grid - This grid</li>
55946              * <li>record - The record being edited</li>
55947              * <li>field - The field name being edited</li>
55948              * <li>value - The value being set</li>
55949              * <li>originalValue - The original value for the field, before the edit.</li>
55950              * <li>row - The grid row index</li>
55951              * <li>column - The grid column index</li>
55952              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55953              * </ul>
55954              * @param {Object} e An edit event (see above for description)
55955              */
55956             "validateedit" : true
55957         });
55958     this.on("bodyscroll", this.stopEditing,  this);
55959     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55960 };
55961
55962 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55963     /**
55964      * @cfg {Number} clicksToEdit
55965      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55966      */
55967     clicksToEdit: 2,
55968
55969     // private
55970     isEditor : true,
55971     // private
55972     trackMouseOver: false, // causes very odd FF errors
55973
55974     onCellDblClick : function(g, row, col){
55975         this.startEditing(row, col);
55976     },
55977
55978     onEditComplete : function(ed, value, startValue){
55979         this.editing = false;
55980         this.activeEditor = null;
55981         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55982         var r = ed.record;
55983         var field = this.colModel.getDataIndex(ed.col);
55984         var e = {
55985             grid: this,
55986             record: r,
55987             field: field,
55988             originalValue: startValue,
55989             value: value,
55990             row: ed.row,
55991             column: ed.col,
55992             cancel:false,
55993             editor: ed
55994         };
55995         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55996         cell.show();
55997           
55998         if(String(value) !== String(startValue)){
55999             
56000             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56001                 r.set(field, e.value);
56002                 // if we are dealing with a combo box..
56003                 // then we also set the 'name' colum to be the displayField
56004                 if (ed.field.displayField && ed.field.name) {
56005                     r.set(ed.field.name, ed.field.el.dom.value);
56006                 }
56007                 
56008                 delete e.cancel; //?? why!!!
56009                 this.fireEvent("afteredit", e);
56010             }
56011         } else {
56012             this.fireEvent("afteredit", e); // always fire it!
56013         }
56014         this.view.focusCell(ed.row, ed.col);
56015     },
56016
56017     /**
56018      * Starts editing the specified for the specified row/column
56019      * @param {Number} rowIndex
56020      * @param {Number} colIndex
56021      */
56022     startEditing : function(row, col){
56023         this.stopEditing();
56024         if(this.colModel.isCellEditable(col, row)){
56025             this.view.ensureVisible(row, col, true);
56026           
56027             var r = this.dataSource.getAt(row);
56028             var field = this.colModel.getDataIndex(col);
56029             var cell = Roo.get(this.view.getCell(row,col));
56030             var e = {
56031                 grid: this,
56032                 record: r,
56033                 field: field,
56034                 value: r.data[field],
56035                 row: row,
56036                 column: col,
56037                 cancel:false 
56038             };
56039             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56040                 this.editing = true;
56041                 var ed = this.colModel.getCellEditor(col, row);
56042                 
56043                 if (!ed) {
56044                     return;
56045                 }
56046                 if(!ed.rendered){
56047                     ed.render(ed.parentEl || document.body);
56048                 }
56049                 ed.field.reset();
56050                
56051                 cell.hide();
56052                 
56053                 (function(){ // complex but required for focus issues in safari, ie and opera
56054                     ed.row = row;
56055                     ed.col = col;
56056                     ed.record = r;
56057                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56058                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56059                     this.activeEditor = ed;
56060                     var v = r.data[field];
56061                     ed.startEdit(this.view.getCell(row, col), v);
56062                     // combo's with 'displayField and name set
56063                     if (ed.field.displayField && ed.field.name) {
56064                         ed.field.el.dom.value = r.data[ed.field.name];
56065                     }
56066                     
56067                     
56068                 }).defer(50, this);
56069             }
56070         }
56071     },
56072         
56073     /**
56074      * Stops any active editing
56075      */
56076     stopEditing : function(){
56077         if(this.activeEditor){
56078             this.activeEditor.completeEdit();
56079         }
56080         this.activeEditor = null;
56081     },
56082         
56083          /**
56084      * Called to get grid's drag proxy text, by default returns this.ddText.
56085      * @return {String}
56086      */
56087     getDragDropText : function(){
56088         var count = this.selModel.getSelectedCell() ? 1 : 0;
56089         return String.format(this.ddText, count, count == 1 ? '' : 's');
56090     }
56091         
56092 });/*
56093  * Based on:
56094  * Ext JS Library 1.1.1
56095  * Copyright(c) 2006-2007, Ext JS, LLC.
56096  *
56097  * Originally Released Under LGPL - original licence link has changed is not relivant.
56098  *
56099  * Fork - LGPL
56100  * <script type="text/javascript">
56101  */
56102
56103 // private - not really -- you end up using it !
56104 // This is a support class used internally by the Grid components
56105
56106 /**
56107  * @class Roo.grid.GridEditor
56108  * @extends Roo.Editor
56109  * Class for creating and editable grid elements.
56110  * @param {Object} config any settings (must include field)
56111  */
56112 Roo.grid.GridEditor = function(field, config){
56113     if (!config && field.field) {
56114         config = field;
56115         field = Roo.factory(config.field, Roo.form);
56116     }
56117     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56118     field.monitorTab = false;
56119 };
56120
56121 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56122     
56123     /**
56124      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56125      */
56126     
56127     alignment: "tl-tl",
56128     autoSize: "width",
56129     hideEl : false,
56130     cls: "x-small-editor x-grid-editor",
56131     shim:false,
56132     shadow:"frame"
56133 });/*
56134  * Based on:
56135  * Ext JS Library 1.1.1
56136  * Copyright(c) 2006-2007, Ext JS, LLC.
56137  *
56138  * Originally Released Under LGPL - original licence link has changed is not relivant.
56139  *
56140  * Fork - LGPL
56141  * <script type="text/javascript">
56142  */
56143   
56144
56145   
56146 Roo.grid.PropertyRecord = Roo.data.Record.create([
56147     {name:'name',type:'string'},  'value'
56148 ]);
56149
56150
56151 Roo.grid.PropertyStore = function(grid, source){
56152     this.grid = grid;
56153     this.store = new Roo.data.Store({
56154         recordType : Roo.grid.PropertyRecord
56155     });
56156     this.store.on('update', this.onUpdate,  this);
56157     if(source){
56158         this.setSource(source);
56159     }
56160     Roo.grid.PropertyStore.superclass.constructor.call(this);
56161 };
56162
56163
56164
56165 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56166     setSource : function(o){
56167         this.source = o;
56168         this.store.removeAll();
56169         var data = [];
56170         for(var k in o){
56171             if(this.isEditableValue(o[k])){
56172                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56173             }
56174         }
56175         this.store.loadRecords({records: data}, {}, true);
56176     },
56177
56178     onUpdate : function(ds, record, type){
56179         if(type == Roo.data.Record.EDIT){
56180             var v = record.data['value'];
56181             var oldValue = record.modified['value'];
56182             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56183                 this.source[record.id] = v;
56184                 record.commit();
56185                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56186             }else{
56187                 record.reject();
56188             }
56189         }
56190     },
56191
56192     getProperty : function(row){
56193        return this.store.getAt(row);
56194     },
56195
56196     isEditableValue: function(val){
56197         if(val && val instanceof Date){
56198             return true;
56199         }else if(typeof val == 'object' || typeof val == 'function'){
56200             return false;
56201         }
56202         return true;
56203     },
56204
56205     setValue : function(prop, value){
56206         this.source[prop] = value;
56207         this.store.getById(prop).set('value', value);
56208     },
56209
56210     getSource : function(){
56211         return this.source;
56212     }
56213 });
56214
56215 Roo.grid.PropertyColumnModel = function(grid, store){
56216     this.grid = grid;
56217     var g = Roo.grid;
56218     g.PropertyColumnModel.superclass.constructor.call(this, [
56219         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56220         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56221     ]);
56222     this.store = store;
56223     this.bselect = Roo.DomHelper.append(document.body, {
56224         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56225             {tag: 'option', value: 'true', html: 'true'},
56226             {tag: 'option', value: 'false', html: 'false'}
56227         ]
56228     });
56229     Roo.id(this.bselect);
56230     var f = Roo.form;
56231     this.editors = {
56232         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56233         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56234         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56235         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56236         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56237     };
56238     this.renderCellDelegate = this.renderCell.createDelegate(this);
56239     this.renderPropDelegate = this.renderProp.createDelegate(this);
56240 };
56241
56242 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56243     
56244     
56245     nameText : 'Name',
56246     valueText : 'Value',
56247     
56248     dateFormat : 'm/j/Y',
56249     
56250     
56251     renderDate : function(dateVal){
56252         return dateVal.dateFormat(this.dateFormat);
56253     },
56254
56255     renderBool : function(bVal){
56256         return bVal ? 'true' : 'false';
56257     },
56258
56259     isCellEditable : function(colIndex, rowIndex){
56260         return colIndex == 1;
56261     },
56262
56263     getRenderer : function(col){
56264         return col == 1 ?
56265             this.renderCellDelegate : this.renderPropDelegate;
56266     },
56267
56268     renderProp : function(v){
56269         return this.getPropertyName(v);
56270     },
56271
56272     renderCell : function(val){
56273         var rv = val;
56274         if(val instanceof Date){
56275             rv = this.renderDate(val);
56276         }else if(typeof val == 'boolean'){
56277             rv = this.renderBool(val);
56278         }
56279         return Roo.util.Format.htmlEncode(rv);
56280     },
56281
56282     getPropertyName : function(name){
56283         var pn = this.grid.propertyNames;
56284         return pn && pn[name] ? pn[name] : name;
56285     },
56286
56287     getCellEditor : function(colIndex, rowIndex){
56288         var p = this.store.getProperty(rowIndex);
56289         var n = p.data['name'], val = p.data['value'];
56290         
56291         if(typeof(this.grid.customEditors[n]) == 'string'){
56292             return this.editors[this.grid.customEditors[n]];
56293         }
56294         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56295             return this.grid.customEditors[n];
56296         }
56297         if(val instanceof Date){
56298             return this.editors['date'];
56299         }else if(typeof val == 'number'){
56300             return this.editors['number'];
56301         }else if(typeof val == 'boolean'){
56302             return this.editors['boolean'];
56303         }else{
56304             return this.editors['string'];
56305         }
56306     }
56307 });
56308
56309 /**
56310  * @class Roo.grid.PropertyGrid
56311  * @extends Roo.grid.EditorGrid
56312  * This class represents the  interface of a component based property grid control.
56313  * <br><br>Usage:<pre><code>
56314  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56315       
56316  });
56317  // set any options
56318  grid.render();
56319  * </code></pre>
56320   
56321  * @constructor
56322  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56323  * The container MUST have some type of size defined for the grid to fill. The container will be
56324  * automatically set to position relative if it isn't already.
56325  * @param {Object} config A config object that sets properties on this grid.
56326  */
56327 Roo.grid.PropertyGrid = function(container, config){
56328     config = config || {};
56329     var store = new Roo.grid.PropertyStore(this);
56330     this.store = store;
56331     var cm = new Roo.grid.PropertyColumnModel(this, store);
56332     store.store.sort('name', 'ASC');
56333     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56334         ds: store.store,
56335         cm: cm,
56336         enableColLock:false,
56337         enableColumnMove:false,
56338         stripeRows:false,
56339         trackMouseOver: false,
56340         clicksToEdit:1
56341     }, config));
56342     this.getGridEl().addClass('x-props-grid');
56343     this.lastEditRow = null;
56344     this.on('columnresize', this.onColumnResize, this);
56345     this.addEvents({
56346          /**
56347              * @event beforepropertychange
56348              * Fires before a property changes (return false to stop?)
56349              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56350              * @param {String} id Record Id
56351              * @param {String} newval New Value
56352          * @param {String} oldval Old Value
56353              */
56354         "beforepropertychange": true,
56355         /**
56356              * @event propertychange
56357              * Fires after a property changes
56358              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56359              * @param {String} id Record Id
56360              * @param {String} newval New Value
56361          * @param {String} oldval Old Value
56362              */
56363         "propertychange": true
56364     });
56365     this.customEditors = this.customEditors || {};
56366 };
56367 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56368     
56369      /**
56370      * @cfg {Object} customEditors map of colnames=> custom editors.
56371      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56372      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56373      * false disables editing of the field.
56374          */
56375     
56376       /**
56377      * @cfg {Object} propertyNames map of property Names to their displayed value
56378          */
56379     
56380     render : function(){
56381         Roo.grid.PropertyGrid.superclass.render.call(this);
56382         this.autoSize.defer(100, this);
56383     },
56384
56385     autoSize : function(){
56386         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56387         if(this.view){
56388             this.view.fitColumns();
56389         }
56390     },
56391
56392     onColumnResize : function(){
56393         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56394         this.autoSize();
56395     },
56396     /**
56397      * Sets the data for the Grid
56398      * accepts a Key => Value object of all the elements avaiable.
56399      * @param {Object} data  to appear in grid.
56400      */
56401     setSource : function(source){
56402         this.store.setSource(source);
56403         //this.autoSize();
56404     },
56405     /**
56406      * Gets all the data from the grid.
56407      * @return {Object} data  data stored in grid
56408      */
56409     getSource : function(){
56410         return this.store.getSource();
56411     }
56412 });/*
56413   
56414  * Licence LGPL
56415  
56416  */
56417  
56418 /**
56419  * @class Roo.grid.Calendar
56420  * @extends Roo.util.Grid
56421  * This class extends the Grid to provide a calendar widget
56422  * <br><br>Usage:<pre><code>
56423  var grid = new Roo.grid.Calendar("my-container-id", {
56424      ds: myDataStore,
56425      cm: myColModel,
56426      selModel: mySelectionModel,
56427      autoSizeColumns: true,
56428      monitorWindowResize: false,
56429      trackMouseOver: true
56430      eventstore : real data store..
56431  });
56432  // set any options
56433  grid.render();
56434   
56435   * @constructor
56436  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56437  * The container MUST have some type of size defined for the grid to fill. The container will be
56438  * automatically set to position relative if it isn't already.
56439  * @param {Object} config A config object that sets properties on this grid.
56440  */
56441 Roo.grid.Calendar = function(container, config){
56442         // initialize the container
56443         this.container = Roo.get(container);
56444         this.container.update("");
56445         this.container.setStyle("overflow", "hidden");
56446     this.container.addClass('x-grid-container');
56447
56448     this.id = this.container.id;
56449
56450     Roo.apply(this, config);
56451     // check and correct shorthanded configs
56452     
56453     var rows = [];
56454     var d =1;
56455     for (var r = 0;r < 6;r++) {
56456         
56457         rows[r]=[];
56458         for (var c =0;c < 7;c++) {
56459             rows[r][c]= '';
56460         }
56461     }
56462     if (this.eventStore) {
56463         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56464         this.eventStore.on('load',this.onLoad, this);
56465         this.eventStore.on('beforeload',this.clearEvents, this);
56466          
56467     }
56468     
56469     this.dataSource = new Roo.data.Store({
56470             proxy: new Roo.data.MemoryProxy(rows),
56471             reader: new Roo.data.ArrayReader({}, [
56472                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56473     });
56474
56475     this.dataSource.load();
56476     this.ds = this.dataSource;
56477     this.ds.xmodule = this.xmodule || false;
56478     
56479     
56480     var cellRender = function(v,x,r)
56481     {
56482         return String.format(
56483             '<div class="fc-day  fc-widget-content"><div>' +
56484                 '<div class="fc-event-container"></div>' +
56485                 '<div class="fc-day-number">{0}</div>'+
56486                 
56487                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56488             '</div></div>', v);
56489     
56490     }
56491     
56492     
56493     this.colModel = new Roo.grid.ColumnModel( [
56494         {
56495             xtype: 'ColumnModel',
56496             xns: Roo.grid,
56497             dataIndex : 'weekday0',
56498             header : 'Sunday',
56499             renderer : cellRender
56500         },
56501         {
56502             xtype: 'ColumnModel',
56503             xns: Roo.grid,
56504             dataIndex : 'weekday1',
56505             header : 'Monday',
56506             renderer : cellRender
56507         },
56508         {
56509             xtype: 'ColumnModel',
56510             xns: Roo.grid,
56511             dataIndex : 'weekday2',
56512             header : 'Tuesday',
56513             renderer : cellRender
56514         },
56515         {
56516             xtype: 'ColumnModel',
56517             xns: Roo.grid,
56518             dataIndex : 'weekday3',
56519             header : 'Wednesday',
56520             renderer : cellRender
56521         },
56522         {
56523             xtype: 'ColumnModel',
56524             xns: Roo.grid,
56525             dataIndex : 'weekday4',
56526             header : 'Thursday',
56527             renderer : cellRender
56528         },
56529         {
56530             xtype: 'ColumnModel',
56531             xns: Roo.grid,
56532             dataIndex : 'weekday5',
56533             header : 'Friday',
56534             renderer : cellRender
56535         },
56536         {
56537             xtype: 'ColumnModel',
56538             xns: Roo.grid,
56539             dataIndex : 'weekday6',
56540             header : 'Saturday',
56541             renderer : cellRender
56542         }
56543     ]);
56544     this.cm = this.colModel;
56545     this.cm.xmodule = this.xmodule || false;
56546  
56547         
56548           
56549     //this.selModel = new Roo.grid.CellSelectionModel();
56550     //this.sm = this.selModel;
56551     //this.selModel.init(this);
56552     
56553     
56554     if(this.width){
56555         this.container.setWidth(this.width);
56556     }
56557
56558     if(this.height){
56559         this.container.setHeight(this.height);
56560     }
56561     /** @private */
56562         this.addEvents({
56563         // raw events
56564         /**
56565          * @event click
56566          * The raw click event for the entire grid.
56567          * @param {Roo.EventObject} e
56568          */
56569         "click" : true,
56570         /**
56571          * @event dblclick
56572          * The raw dblclick event for the entire grid.
56573          * @param {Roo.EventObject} e
56574          */
56575         "dblclick" : true,
56576         /**
56577          * @event contextmenu
56578          * The raw contextmenu event for the entire grid.
56579          * @param {Roo.EventObject} e
56580          */
56581         "contextmenu" : true,
56582         /**
56583          * @event mousedown
56584          * The raw mousedown event for the entire grid.
56585          * @param {Roo.EventObject} e
56586          */
56587         "mousedown" : true,
56588         /**
56589          * @event mouseup
56590          * The raw mouseup event for the entire grid.
56591          * @param {Roo.EventObject} e
56592          */
56593         "mouseup" : true,
56594         /**
56595          * @event mouseover
56596          * The raw mouseover event for the entire grid.
56597          * @param {Roo.EventObject} e
56598          */
56599         "mouseover" : true,
56600         /**
56601          * @event mouseout
56602          * The raw mouseout event for the entire grid.
56603          * @param {Roo.EventObject} e
56604          */
56605         "mouseout" : true,
56606         /**
56607          * @event keypress
56608          * The raw keypress event for the entire grid.
56609          * @param {Roo.EventObject} e
56610          */
56611         "keypress" : true,
56612         /**
56613          * @event keydown
56614          * The raw keydown event for the entire grid.
56615          * @param {Roo.EventObject} e
56616          */
56617         "keydown" : true,
56618
56619         // custom events
56620
56621         /**
56622          * @event cellclick
56623          * Fires when a cell is clicked
56624          * @param {Grid} this
56625          * @param {Number} rowIndex
56626          * @param {Number} columnIndex
56627          * @param {Roo.EventObject} e
56628          */
56629         "cellclick" : true,
56630         /**
56631          * @event celldblclick
56632          * Fires when a cell is double clicked
56633          * @param {Grid} this
56634          * @param {Number} rowIndex
56635          * @param {Number} columnIndex
56636          * @param {Roo.EventObject} e
56637          */
56638         "celldblclick" : true,
56639         /**
56640          * @event rowclick
56641          * Fires when a row is clicked
56642          * @param {Grid} this
56643          * @param {Number} rowIndex
56644          * @param {Roo.EventObject} e
56645          */
56646         "rowclick" : true,
56647         /**
56648          * @event rowdblclick
56649          * Fires when a row is double clicked
56650          * @param {Grid} this
56651          * @param {Number} rowIndex
56652          * @param {Roo.EventObject} e
56653          */
56654         "rowdblclick" : true,
56655         /**
56656          * @event headerclick
56657          * Fires when a header is clicked
56658          * @param {Grid} this
56659          * @param {Number} columnIndex
56660          * @param {Roo.EventObject} e
56661          */
56662         "headerclick" : true,
56663         /**
56664          * @event headerdblclick
56665          * Fires when a header cell is double clicked
56666          * @param {Grid} this
56667          * @param {Number} columnIndex
56668          * @param {Roo.EventObject} e
56669          */
56670         "headerdblclick" : true,
56671         /**
56672          * @event rowcontextmenu
56673          * Fires when a row is right clicked
56674          * @param {Grid} this
56675          * @param {Number} rowIndex
56676          * @param {Roo.EventObject} e
56677          */
56678         "rowcontextmenu" : true,
56679         /**
56680          * @event cellcontextmenu
56681          * Fires when a cell is right clicked
56682          * @param {Grid} this
56683          * @param {Number} rowIndex
56684          * @param {Number} cellIndex
56685          * @param {Roo.EventObject} e
56686          */
56687          "cellcontextmenu" : true,
56688         /**
56689          * @event headercontextmenu
56690          * Fires when a header is right clicked
56691          * @param {Grid} this
56692          * @param {Number} columnIndex
56693          * @param {Roo.EventObject} e
56694          */
56695         "headercontextmenu" : true,
56696         /**
56697          * @event bodyscroll
56698          * Fires when the body element is scrolled
56699          * @param {Number} scrollLeft
56700          * @param {Number} scrollTop
56701          */
56702         "bodyscroll" : true,
56703         /**
56704          * @event columnresize
56705          * Fires when the user resizes a column
56706          * @param {Number} columnIndex
56707          * @param {Number} newSize
56708          */
56709         "columnresize" : true,
56710         /**
56711          * @event columnmove
56712          * Fires when the user moves a column
56713          * @param {Number} oldIndex
56714          * @param {Number} newIndex
56715          */
56716         "columnmove" : true,
56717         /**
56718          * @event startdrag
56719          * Fires when row(s) start being dragged
56720          * @param {Grid} this
56721          * @param {Roo.GridDD} dd The drag drop object
56722          * @param {event} e The raw browser event
56723          */
56724         "startdrag" : true,
56725         /**
56726          * @event enddrag
56727          * Fires when a drag operation is complete
56728          * @param {Grid} this
56729          * @param {Roo.GridDD} dd The drag drop object
56730          * @param {event} e The raw browser event
56731          */
56732         "enddrag" : true,
56733         /**
56734          * @event dragdrop
56735          * Fires when dragged row(s) are dropped on a valid DD target
56736          * @param {Grid} this
56737          * @param {Roo.GridDD} dd The drag drop object
56738          * @param {String} targetId The target drag drop object
56739          * @param {event} e The raw browser event
56740          */
56741         "dragdrop" : true,
56742         /**
56743          * @event dragover
56744          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56745          * @param {Grid} this
56746          * @param {Roo.GridDD} dd The drag drop object
56747          * @param {String} targetId The target drag drop object
56748          * @param {event} e The raw browser event
56749          */
56750         "dragover" : true,
56751         /**
56752          * @event dragenter
56753          *  Fires when the dragged row(s) first cross another DD target while being dragged
56754          * @param {Grid} this
56755          * @param {Roo.GridDD} dd The drag drop object
56756          * @param {String} targetId The target drag drop object
56757          * @param {event} e The raw browser event
56758          */
56759         "dragenter" : true,
56760         /**
56761          * @event dragout
56762          * Fires when the dragged row(s) leave another DD target while being dragged
56763          * @param {Grid} this
56764          * @param {Roo.GridDD} dd The drag drop object
56765          * @param {String} targetId The target drag drop object
56766          * @param {event} e The raw browser event
56767          */
56768         "dragout" : true,
56769         /**
56770          * @event rowclass
56771          * Fires when a row is rendered, so you can change add a style to it.
56772          * @param {GridView} gridview   The grid view
56773          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56774          */
56775         'rowclass' : true,
56776
56777         /**
56778          * @event render
56779          * Fires when the grid is rendered
56780          * @param {Grid} grid
56781          */
56782         'render' : true,
56783             /**
56784              * @event select
56785              * Fires when a date is selected
56786              * @param {DatePicker} this
56787              * @param {Date} date The selected date
56788              */
56789         'select': true,
56790         /**
56791              * @event monthchange
56792              * Fires when the displayed month changes 
56793              * @param {DatePicker} this
56794              * @param {Date} date The selected month
56795              */
56796         'monthchange': true,
56797         /**
56798              * @event evententer
56799              * Fires when mouse over an event
56800              * @param {Calendar} this
56801              * @param {event} Event
56802              */
56803         'evententer': true,
56804         /**
56805              * @event eventleave
56806              * Fires when the mouse leaves an
56807              * @param {Calendar} this
56808              * @param {event}
56809              */
56810         'eventleave': true,
56811         /**
56812              * @event eventclick
56813              * Fires when the mouse click an
56814              * @param {Calendar} this
56815              * @param {event}
56816              */
56817         'eventclick': true,
56818         /**
56819              * @event eventrender
56820              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56821              * @param {Calendar} this
56822              * @param {data} data to be modified
56823              */
56824         'eventrender': true
56825         
56826     });
56827
56828     Roo.grid.Grid.superclass.constructor.call(this);
56829     this.on('render', function() {
56830         this.view.el.addClass('x-grid-cal'); 
56831         
56832         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56833
56834     },this);
56835     
56836     if (!Roo.grid.Calendar.style) {
56837         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56838             
56839             
56840             '.x-grid-cal .x-grid-col' :  {
56841                 height: 'auto !important',
56842                 'vertical-align': 'top'
56843             },
56844             '.x-grid-cal  .fc-event-hori' : {
56845                 height: '14px'
56846             }
56847              
56848             
56849         }, Roo.id());
56850     }
56851
56852     
56853     
56854 };
56855 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56856     /**
56857      * @cfg {Store} eventStore The store that loads events.
56858      */
56859     eventStore : 25,
56860
56861      
56862     activeDate : false,
56863     startDay : 0,
56864     autoWidth : true,
56865     monitorWindowResize : false,
56866
56867     
56868     resizeColumns : function() {
56869         var col = (this.view.el.getWidth() / 7) - 3;
56870         // loop through cols, and setWidth
56871         for(var i =0 ; i < 7 ; i++){
56872             this.cm.setColumnWidth(i, col);
56873         }
56874     },
56875      setDate :function(date) {
56876         
56877         Roo.log('setDate?');
56878         
56879         this.resizeColumns();
56880         var vd = this.activeDate;
56881         this.activeDate = date;
56882 //        if(vd && this.el){
56883 //            var t = date.getTime();
56884 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56885 //                Roo.log('using add remove');
56886 //                
56887 //                this.fireEvent('monthchange', this, date);
56888 //                
56889 //                this.cells.removeClass("fc-state-highlight");
56890 //                this.cells.each(function(c){
56891 //                   if(c.dateValue == t){
56892 //                       c.addClass("fc-state-highlight");
56893 //                       setTimeout(function(){
56894 //                            try{c.dom.firstChild.focus();}catch(e){}
56895 //                       }, 50);
56896 //                       return false;
56897 //                   }
56898 //                   return true;
56899 //                });
56900 //                return;
56901 //            }
56902 //        }
56903         
56904         var days = date.getDaysInMonth();
56905         
56906         var firstOfMonth = date.getFirstDateOfMonth();
56907         var startingPos = firstOfMonth.getDay()-this.startDay;
56908         
56909         if(startingPos < this.startDay){
56910             startingPos += 7;
56911         }
56912         
56913         var pm = date.add(Date.MONTH, -1);
56914         var prevStart = pm.getDaysInMonth()-startingPos;
56915 //        
56916         
56917         
56918         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56919         
56920         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
56921         //this.cells.addClassOnOver('fc-state-hover');
56922         
56923         var cells = this.cells.elements;
56924         var textEls = this.textNodes;
56925         
56926         //Roo.each(cells, function(cell){
56927         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
56928         //});
56929         
56930         days += startingPos;
56931
56932         // convert everything to numbers so it's fast
56933         var day = 86400000;
56934         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
56935         //Roo.log(d);
56936         //Roo.log(pm);
56937         //Roo.log(prevStart);
56938         
56939         var today = new Date().clearTime().getTime();
56940         var sel = date.clearTime().getTime();
56941         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
56942         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
56943         var ddMatch = this.disabledDatesRE;
56944         var ddText = this.disabledDatesText;
56945         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
56946         var ddaysText = this.disabledDaysText;
56947         var format = this.format;
56948         
56949         var setCellClass = function(cal, cell){
56950             
56951             //Roo.log('set Cell Class');
56952             cell.title = "";
56953             var t = d.getTime();
56954             
56955             //Roo.log(d);
56956             
56957             
56958             cell.dateValue = t;
56959             if(t == today){
56960                 cell.className += " fc-today";
56961                 cell.className += " fc-state-highlight";
56962                 cell.title = cal.todayText;
56963             }
56964             if(t == sel){
56965                 // disable highlight in other month..
56966                 cell.className += " fc-state-highlight";
56967                 
56968             }
56969             // disabling
56970             if(t < min) {
56971                 //cell.className = " fc-state-disabled";
56972                 cell.title = cal.minText;
56973                 return;
56974             }
56975             if(t > max) {
56976                 //cell.className = " fc-state-disabled";
56977                 cell.title = cal.maxText;
56978                 return;
56979             }
56980             if(ddays){
56981                 if(ddays.indexOf(d.getDay()) != -1){
56982                     // cell.title = ddaysText;
56983                    // cell.className = " fc-state-disabled";
56984                 }
56985             }
56986             if(ddMatch && format){
56987                 var fvalue = d.dateFormat(format);
56988                 if(ddMatch.test(fvalue)){
56989                     cell.title = ddText.replace("%0", fvalue);
56990                    cell.className = " fc-state-disabled";
56991                 }
56992             }
56993             
56994             if (!cell.initialClassName) {
56995                 cell.initialClassName = cell.dom.className;
56996             }
56997             
56998             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
56999         };
57000
57001         var i = 0;
57002         
57003         for(; i < startingPos; i++) {
57004             cells[i].dayName =  (++prevStart);
57005             Roo.log(textEls[i]);
57006             d.setDate(d.getDate()+1);
57007             
57008             //cells[i].className = "fc-past fc-other-month";
57009             setCellClass(this, cells[i]);
57010         }
57011         
57012         var intDay = 0;
57013         
57014         for(; i < days; i++){
57015             intDay = i - startingPos + 1;
57016             cells[i].dayName =  (intDay);
57017             d.setDate(d.getDate()+1);
57018             
57019             cells[i].className = ''; // "x-date-active";
57020             setCellClass(this, cells[i]);
57021         }
57022         var extraDays = 0;
57023         
57024         for(; i < 42; i++) {
57025             //textEls[i].innerHTML = (++extraDays);
57026             
57027             d.setDate(d.getDate()+1);
57028             cells[i].dayName = (++extraDays);
57029             cells[i].className = "fc-future fc-other-month";
57030             setCellClass(this, cells[i]);
57031         }
57032         
57033         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57034         
57035         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57036         
57037         // this will cause all the cells to mis
57038         var rows= [];
57039         var i =0;
57040         for (var r = 0;r < 6;r++) {
57041             for (var c =0;c < 7;c++) {
57042                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57043             }    
57044         }
57045         
57046         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57047         for(i=0;i<cells.length;i++) {
57048             
57049             this.cells.elements[i].dayName = cells[i].dayName ;
57050             this.cells.elements[i].className = cells[i].className;
57051             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57052             this.cells.elements[i].title = cells[i].title ;
57053             this.cells.elements[i].dateValue = cells[i].dateValue ;
57054         }
57055         
57056         
57057         
57058         
57059         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57060         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57061         
57062         ////if(totalRows != 6){
57063             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57064            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57065        // }
57066         
57067         this.fireEvent('monthchange', this, date);
57068         
57069         
57070     },
57071  /**
57072      * Returns the grid's SelectionModel.
57073      * @return {SelectionModel}
57074      */
57075     getSelectionModel : function(){
57076         if(!this.selModel){
57077             this.selModel = new Roo.grid.CellSelectionModel();
57078         }
57079         return this.selModel;
57080     },
57081
57082     load: function() {
57083         this.eventStore.load()
57084         
57085         
57086         
57087     },
57088     
57089     findCell : function(dt) {
57090         dt = dt.clearTime().getTime();
57091         var ret = false;
57092         this.cells.each(function(c){
57093             //Roo.log("check " +c.dateValue + '?=' + dt);
57094             if(c.dateValue == dt){
57095                 ret = c;
57096                 return false;
57097             }
57098             return true;
57099         });
57100         
57101         return ret;
57102     },
57103     
57104     findCells : function(rec) {
57105         var s = rec.data.start_dt.clone().clearTime().getTime();
57106        // Roo.log(s);
57107         var e= rec.data.end_dt.clone().clearTime().getTime();
57108        // Roo.log(e);
57109         var ret = [];
57110         this.cells.each(function(c){
57111              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57112             
57113             if(c.dateValue > e){
57114                 return ;
57115             }
57116             if(c.dateValue < s){
57117                 return ;
57118             }
57119             ret.push(c);
57120         });
57121         
57122         return ret;    
57123     },
57124     
57125     findBestRow: function(cells)
57126     {
57127         var ret = 0;
57128         
57129         for (var i =0 ; i < cells.length;i++) {
57130             ret  = Math.max(cells[i].rows || 0,ret);
57131         }
57132         return ret;
57133         
57134     },
57135     
57136     
57137     addItem : function(rec)
57138     {
57139         // look for vertical location slot in
57140         var cells = this.findCells(rec);
57141         
57142         rec.row = this.findBestRow(cells);
57143         
57144         // work out the location.
57145         
57146         var crow = false;
57147         var rows = [];
57148         for(var i =0; i < cells.length; i++) {
57149             if (!crow) {
57150                 crow = {
57151                     start : cells[i],
57152                     end :  cells[i]
57153                 };
57154                 continue;
57155             }
57156             if (crow.start.getY() == cells[i].getY()) {
57157                 // on same row.
57158                 crow.end = cells[i];
57159                 continue;
57160             }
57161             // different row.
57162             rows.push(crow);
57163             crow = {
57164                 start: cells[i],
57165                 end : cells[i]
57166             };
57167             
57168         }
57169         
57170         rows.push(crow);
57171         rec.els = [];
57172         rec.rows = rows;
57173         rec.cells = cells;
57174         for (var i = 0; i < cells.length;i++) {
57175             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57176             
57177         }
57178         
57179         
57180     },
57181     
57182     clearEvents: function() {
57183         
57184         if (!this.eventStore.getCount()) {
57185             return;
57186         }
57187         // reset number of rows in cells.
57188         Roo.each(this.cells.elements, function(c){
57189             c.rows = 0;
57190         });
57191         
57192         this.eventStore.each(function(e) {
57193             this.clearEvent(e);
57194         },this);
57195         
57196     },
57197     
57198     clearEvent : function(ev)
57199     {
57200         if (ev.els) {
57201             Roo.each(ev.els, function(el) {
57202                 el.un('mouseenter' ,this.onEventEnter, this);
57203                 el.un('mouseleave' ,this.onEventLeave, this);
57204                 el.remove();
57205             },this);
57206             ev.els = [];
57207         }
57208     },
57209     
57210     
57211     renderEvent : function(ev,ctr) {
57212         if (!ctr) {
57213              ctr = this.view.el.select('.fc-event-container',true).first();
57214         }
57215         
57216          
57217         this.clearEvent(ev);
57218             //code
57219        
57220         
57221         
57222         ev.els = [];
57223         var cells = ev.cells;
57224         var rows = ev.rows;
57225         this.fireEvent('eventrender', this, ev);
57226         
57227         for(var i =0; i < rows.length; i++) {
57228             
57229             cls = '';
57230             if (i == 0) {
57231                 cls += ' fc-event-start';
57232             }
57233             if ((i+1) == rows.length) {
57234                 cls += ' fc-event-end';
57235             }
57236             
57237             //Roo.log(ev.data);
57238             // how many rows should it span..
57239             var cg = this.eventTmpl.append(ctr,Roo.apply({
57240                 fccls : cls
57241                 
57242             }, ev.data) , true);
57243             
57244             
57245             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57246             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57247             cg.on('click', this.onEventClick, this, ev);
57248             
57249             ev.els.push(cg);
57250             
57251             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57252             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57253             //Roo.log(cg);
57254              
57255             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57256             cg.setWidth(ebox.right - sbox.x -2);
57257         }
57258     },
57259     
57260     renderEvents: function()
57261     {   
57262         // first make sure there is enough space..
57263         
57264         if (!this.eventTmpl) {
57265             this.eventTmpl = new Roo.Template(
57266                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57267                     '<div class="fc-event-inner">' +
57268                         '<span class="fc-event-time">{time}</span>' +
57269                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57270                     '</div>' +
57271                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57272                 '</div>'
57273             );
57274                 
57275         }
57276                
57277         
57278         
57279         this.cells.each(function(c) {
57280             //Roo.log(c.select('.fc-day-content div',true).first());
57281             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57282         });
57283         
57284         var ctr = this.view.el.select('.fc-event-container',true).first();
57285         
57286         var cls;
57287         this.eventStore.each(function(ev){
57288             
57289             this.renderEvent(ev);
57290              
57291              
57292         }, this);
57293         this.view.layout();
57294         
57295     },
57296     
57297     onEventEnter: function (e, el,event,d) {
57298         this.fireEvent('evententer', this, el, event);
57299     },
57300     
57301     onEventLeave: function (e, el,event,d) {
57302         this.fireEvent('eventleave', this, el, event);
57303     },
57304     
57305     onEventClick: function (e, el,event,d) {
57306         this.fireEvent('eventclick', this, el, event);
57307     },
57308     
57309     onMonthChange: function () {
57310         this.store.load();
57311     },
57312     
57313     onLoad: function () {
57314         
57315         //Roo.log('calendar onload');
57316 //         
57317         if(this.eventStore.getCount() > 0){
57318             
57319            
57320             
57321             this.eventStore.each(function(d){
57322                 
57323                 
57324                 // FIXME..
57325                 var add =   d.data;
57326                 if (typeof(add.end_dt) == 'undefined')  {
57327                     Roo.log("Missing End time in calendar data: ");
57328                     Roo.log(d);
57329                     return;
57330                 }
57331                 if (typeof(add.start_dt) == 'undefined')  {
57332                     Roo.log("Missing Start time in calendar data: ");
57333                     Roo.log(d);
57334                     return;
57335                 }
57336                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57337                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57338                 add.id = add.id || d.id;
57339                 add.title = add.title || '??';
57340                 
57341                 this.addItem(d);
57342                 
57343              
57344             },this);
57345         }
57346         
57347         this.renderEvents();
57348     }
57349     
57350
57351 });
57352 /*
57353  grid : {
57354                 xtype: 'Grid',
57355                 xns: Roo.grid,
57356                 listeners : {
57357                     render : function ()
57358                     {
57359                         _this.grid = this;
57360                         
57361                         if (!this.view.el.hasClass('course-timesheet')) {
57362                             this.view.el.addClass('course-timesheet');
57363                         }
57364                         if (this.tsStyle) {
57365                             this.ds.load({});
57366                             return; 
57367                         }
57368                         Roo.log('width');
57369                         Roo.log(_this.grid.view.el.getWidth());
57370                         
57371                         
57372                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57373                             '.course-timesheet .x-grid-row' : {
57374                                 height: '80px'
57375                             },
57376                             '.x-grid-row td' : {
57377                                 'vertical-align' : 0
57378                             },
57379                             '.course-edit-link' : {
57380                                 'color' : 'blue',
57381                                 'text-overflow' : 'ellipsis',
57382                                 'overflow' : 'hidden',
57383                                 'white-space' : 'nowrap',
57384                                 'cursor' : 'pointer'
57385                             },
57386                             '.sub-link' : {
57387                                 'color' : 'green'
57388                             },
57389                             '.de-act-sup-link' : {
57390                                 'color' : 'purple',
57391                                 'text-decoration' : 'line-through'
57392                             },
57393                             '.de-act-link' : {
57394                                 'color' : 'red',
57395                                 'text-decoration' : 'line-through'
57396                             },
57397                             '.course-timesheet .course-highlight' : {
57398                                 'border-top-style': 'dashed !important',
57399                                 'border-bottom-bottom': 'dashed !important'
57400                             },
57401                             '.course-timesheet .course-item' : {
57402                                 'font-family'   : 'tahoma, arial, helvetica',
57403                                 'font-size'     : '11px',
57404                                 'overflow'      : 'hidden',
57405                                 'padding-left'  : '10px',
57406                                 'padding-right' : '10px',
57407                                 'padding-top' : '10px' 
57408                             }
57409                             
57410                         }, Roo.id());
57411                                 this.ds.load({});
57412                     }
57413                 },
57414                 autoWidth : true,
57415                 monitorWindowResize : false,
57416                 cellrenderer : function(v,x,r)
57417                 {
57418                     return v;
57419                 },
57420                 sm : {
57421                     xtype: 'CellSelectionModel',
57422                     xns: Roo.grid
57423                 },
57424                 dataSource : {
57425                     xtype: 'Store',
57426                     xns: Roo.data,
57427                     listeners : {
57428                         beforeload : function (_self, options)
57429                         {
57430                             options.params = options.params || {};
57431                             options.params._month = _this.monthField.getValue();
57432                             options.params.limit = 9999;
57433                             options.params['sort'] = 'when_dt';    
57434                             options.params['dir'] = 'ASC';    
57435                             this.proxy.loadResponse = this.loadResponse;
57436                             Roo.log("load?");
57437                             //this.addColumns();
57438                         },
57439                         load : function (_self, records, options)
57440                         {
57441                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57442                                 // if you click on the translation.. you can edit it...
57443                                 var el = Roo.get(this);
57444                                 var id = el.dom.getAttribute('data-id');
57445                                 var d = el.dom.getAttribute('data-date');
57446                                 var t = el.dom.getAttribute('data-time');
57447                                 //var id = this.child('span').dom.textContent;
57448                                 
57449                                 //Roo.log(this);
57450                                 Pman.Dialog.CourseCalendar.show({
57451                                     id : id,
57452                                     when_d : d,
57453                                     when_t : t,
57454                                     productitem_active : id ? 1 : 0
57455                                 }, function() {
57456                                     _this.grid.ds.load({});
57457                                 });
57458                            
57459                            });
57460                            
57461                            _this.panel.fireEvent('resize', [ '', '' ]);
57462                         }
57463                     },
57464                     loadResponse : function(o, success, response){
57465                             // this is overridden on before load..
57466                             
57467                             Roo.log("our code?");       
57468                             //Roo.log(success);
57469                             //Roo.log(response)
57470                             delete this.activeRequest;
57471                             if(!success){
57472                                 this.fireEvent("loadexception", this, o, response);
57473                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57474                                 return;
57475                             }
57476                             var result;
57477                             try {
57478                                 result = o.reader.read(response);
57479                             }catch(e){
57480                                 Roo.log("load exception?");
57481                                 this.fireEvent("loadexception", this, o, response, e);
57482                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57483                                 return;
57484                             }
57485                             Roo.log("ready...");        
57486                             // loop through result.records;
57487                             // and set this.tdate[date] = [] << array of records..
57488                             _this.tdata  = {};
57489                             Roo.each(result.records, function(r){
57490                                 //Roo.log(r.data);
57491                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57492                                     _this.tdata[r.data.when_dt.format('j')] = [];
57493                                 }
57494                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57495                             });
57496                             
57497                             //Roo.log(_this.tdata);
57498                             
57499                             result.records = [];
57500                             result.totalRecords = 6;
57501                     
57502                             // let's generate some duumy records for the rows.
57503                             //var st = _this.dateField.getValue();
57504                             
57505                             // work out monday..
57506                             //st = st.add(Date.DAY, -1 * st.format('w'));
57507                             
57508                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57509                             
57510                             var firstOfMonth = date.getFirstDayOfMonth();
57511                             var days = date.getDaysInMonth();
57512                             var d = 1;
57513                             var firstAdded = false;
57514                             for (var i = 0; i < result.totalRecords ; i++) {
57515                                 //var d= st.add(Date.DAY, i);
57516                                 var row = {};
57517                                 var added = 0;
57518                                 for(var w = 0 ; w < 7 ; w++){
57519                                     if(!firstAdded && firstOfMonth != w){
57520                                         continue;
57521                                     }
57522                                     if(d > days){
57523                                         continue;
57524                                     }
57525                                     firstAdded = true;
57526                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57527                                     row['weekday'+w] = String.format(
57528                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57529                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57530                                                     d,
57531                                                     date.format('Y-m-')+dd
57532                                                 );
57533                                     added++;
57534                                     if(typeof(_this.tdata[d]) != 'undefined'){
57535                                         Roo.each(_this.tdata[d], function(r){
57536                                             var is_sub = '';
57537                                             var deactive = '';
57538                                             var id = r.id;
57539                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57540                                             if(r.parent_id*1>0){
57541                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57542                                                 id = r.parent_id;
57543                                             }
57544                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57545                                                 deactive = 'de-act-link';
57546                                             }
57547                                             
57548                                             row['weekday'+w] += String.format(
57549                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57550                                                     id, //0
57551                                                     r.product_id_name, //1
57552                                                     r.when_dt.format('h:ia'), //2
57553                                                     is_sub, //3
57554                                                     deactive, //4
57555                                                     desc // 5
57556                                             );
57557                                         });
57558                                     }
57559                                     d++;
57560                                 }
57561                                 
57562                                 // only do this if something added..
57563                                 if(added > 0){ 
57564                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57565                                 }
57566                                 
57567                                 
57568                                 // push it twice. (second one with an hour..
57569                                 
57570                             }
57571                             //Roo.log(result);
57572                             this.fireEvent("load", this, o, o.request.arg);
57573                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57574                         },
57575                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57576                     proxy : {
57577                         xtype: 'HttpProxy',
57578                         xns: Roo.data,
57579                         method : 'GET',
57580                         url : baseURL + '/Roo/Shop_course.php'
57581                     },
57582                     reader : {
57583                         xtype: 'JsonReader',
57584                         xns: Roo.data,
57585                         id : 'id',
57586                         fields : [
57587                             {
57588                                 'name': 'id',
57589                                 'type': 'int'
57590                             },
57591                             {
57592                                 'name': 'when_dt',
57593                                 'type': 'string'
57594                             },
57595                             {
57596                                 'name': 'end_dt',
57597                                 'type': 'string'
57598                             },
57599                             {
57600                                 'name': 'parent_id',
57601                                 'type': 'int'
57602                             },
57603                             {
57604                                 'name': 'product_id',
57605                                 'type': 'int'
57606                             },
57607                             {
57608                                 'name': 'productitem_id',
57609                                 'type': 'int'
57610                             },
57611                             {
57612                                 'name': 'guid',
57613                                 'type': 'int'
57614                             }
57615                         ]
57616                     }
57617                 },
57618                 toolbar : {
57619                     xtype: 'Toolbar',
57620                     xns: Roo,
57621                     items : [
57622                         {
57623                             xtype: 'Button',
57624                             xns: Roo.Toolbar,
57625                             listeners : {
57626                                 click : function (_self, e)
57627                                 {
57628                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57629                                     sd.setMonth(sd.getMonth()-1);
57630                                     _this.monthField.setValue(sd.format('Y-m-d'));
57631                                     _this.grid.ds.load({});
57632                                 }
57633                             },
57634                             text : "Back"
57635                         },
57636                         {
57637                             xtype: 'Separator',
57638                             xns: Roo.Toolbar
57639                         },
57640                         {
57641                             xtype: 'MonthField',
57642                             xns: Roo.form,
57643                             listeners : {
57644                                 render : function (_self)
57645                                 {
57646                                     _this.monthField = _self;
57647                                    // _this.monthField.set  today
57648                                 },
57649                                 select : function (combo, date)
57650                                 {
57651                                     _this.grid.ds.load({});
57652                                 }
57653                             },
57654                             value : (function() { return new Date(); })()
57655                         },
57656                         {
57657                             xtype: 'Separator',
57658                             xns: Roo.Toolbar
57659                         },
57660                         {
57661                             xtype: 'TextItem',
57662                             xns: Roo.Toolbar,
57663                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57664                         },
57665                         {
57666                             xtype: 'Fill',
57667                             xns: Roo.Toolbar
57668                         },
57669                         {
57670                             xtype: 'Button',
57671                             xns: Roo.Toolbar,
57672                             listeners : {
57673                                 click : function (_self, e)
57674                                 {
57675                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57676                                     sd.setMonth(sd.getMonth()+1);
57677                                     _this.monthField.setValue(sd.format('Y-m-d'));
57678                                     _this.grid.ds.load({});
57679                                 }
57680                             },
57681                             text : "Next"
57682                         }
57683                     ]
57684                 },
57685                  
57686             }
57687         };
57688         
57689         *//*
57690  * Based on:
57691  * Ext JS Library 1.1.1
57692  * Copyright(c) 2006-2007, Ext JS, LLC.
57693  *
57694  * Originally Released Under LGPL - original licence link has changed is not relivant.
57695  *
57696  * Fork - LGPL
57697  * <script type="text/javascript">
57698  */
57699  
57700 /**
57701  * @class Roo.LoadMask
57702  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57703  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57704  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57705  * element's UpdateManager load indicator and will be destroyed after the initial load.
57706  * @constructor
57707  * Create a new LoadMask
57708  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57709  * @param {Object} config The config object
57710  */
57711 Roo.LoadMask = function(el, config){
57712     this.el = Roo.get(el);
57713     Roo.apply(this, config);
57714     if(this.store){
57715         this.store.on('beforeload', this.onBeforeLoad, this);
57716         this.store.on('load', this.onLoad, this);
57717         this.store.on('loadexception', this.onLoadException, this);
57718         this.removeMask = false;
57719     }else{
57720         var um = this.el.getUpdateManager();
57721         um.showLoadIndicator = false; // disable the default indicator
57722         um.on('beforeupdate', this.onBeforeLoad, this);
57723         um.on('update', this.onLoad, this);
57724         um.on('failure', this.onLoad, this);
57725         this.removeMask = true;
57726     }
57727 };
57728
57729 Roo.LoadMask.prototype = {
57730     /**
57731      * @cfg {Boolean} removeMask
57732      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57733      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57734      */
57735     /**
57736      * @cfg {String} msg
57737      * The text to display in a centered loading message box (defaults to 'Loading...')
57738      */
57739     msg : 'Loading...',
57740     /**
57741      * @cfg {String} msgCls
57742      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57743      */
57744     msgCls : 'x-mask-loading',
57745
57746     /**
57747      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57748      * @type Boolean
57749      */
57750     disabled: false,
57751
57752     /**
57753      * Disables the mask to prevent it from being displayed
57754      */
57755     disable : function(){
57756        this.disabled = true;
57757     },
57758
57759     /**
57760      * Enables the mask so that it can be displayed
57761      */
57762     enable : function(){
57763         this.disabled = false;
57764     },
57765     
57766     onLoadException : function()
57767     {
57768         Roo.log(arguments);
57769         
57770         if (typeof(arguments[3]) != 'undefined') {
57771             Roo.MessageBox.alert("Error loading",arguments[3]);
57772         } 
57773         /*
57774         try {
57775             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57776                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57777             }   
57778         } catch(e) {
57779             
57780         }
57781         */
57782     
57783         
57784         
57785         this.el.unmask(this.removeMask);
57786     },
57787     // private
57788     onLoad : function()
57789     {
57790         this.el.unmask(this.removeMask);
57791     },
57792
57793     // private
57794     onBeforeLoad : function(){
57795         if(!this.disabled){
57796             this.el.mask(this.msg, this.msgCls);
57797         }
57798     },
57799
57800     // private
57801     destroy : function(){
57802         if(this.store){
57803             this.store.un('beforeload', this.onBeforeLoad, this);
57804             this.store.un('load', this.onLoad, this);
57805             this.store.un('loadexception', this.onLoadException, this);
57806         }else{
57807             var um = this.el.getUpdateManager();
57808             um.un('beforeupdate', this.onBeforeLoad, this);
57809             um.un('update', this.onLoad, this);
57810             um.un('failure', this.onLoad, this);
57811         }
57812     }
57813 };/*
57814  * Based on:
57815  * Ext JS Library 1.1.1
57816  * Copyright(c) 2006-2007, Ext JS, LLC.
57817  *
57818  * Originally Released Under LGPL - original licence link has changed is not relivant.
57819  *
57820  * Fork - LGPL
57821  * <script type="text/javascript">
57822  */
57823
57824
57825 /**
57826  * @class Roo.XTemplate
57827  * @extends Roo.Template
57828  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57829 <pre><code>
57830 var t = new Roo.XTemplate(
57831         '&lt;select name="{name}"&gt;',
57832                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57833         '&lt;/select&gt;'
57834 );
57835  
57836 // then append, applying the master template values
57837  </code></pre>
57838  *
57839  * Supported features:
57840  *
57841  *  Tags:
57842
57843 <pre><code>
57844       {a_variable} - output encoded.
57845       {a_variable.format:("Y-m-d")} - call a method on the variable
57846       {a_variable:raw} - unencoded output
57847       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57848       {a_variable:this.method_on_template(...)} - call a method on the template object.
57849  
57850 </code></pre>
57851  *  The tpl tag:
57852 <pre><code>
57853         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57854         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57855         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57856         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57857   
57858         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57859         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57860 </code></pre>
57861  *      
57862  */
57863 Roo.XTemplate = function()
57864 {
57865     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57866     if (this.html) {
57867         this.compile();
57868     }
57869 };
57870
57871
57872 Roo.extend(Roo.XTemplate, Roo.Template, {
57873
57874     /**
57875      * The various sub templates
57876      */
57877     tpls : false,
57878     /**
57879      *
57880      * basic tag replacing syntax
57881      * WORD:WORD()
57882      *
57883      * // you can fake an object call by doing this
57884      *  x.t:(test,tesT) 
57885      * 
57886      */
57887     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57888
57889     /**
57890      * compile the template
57891      *
57892      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57893      *
57894      */
57895     compile: function()
57896     {
57897         var s = this.html;
57898      
57899         s = ['<tpl>', s, '</tpl>'].join('');
57900     
57901         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57902             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57903             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57904             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57905             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57906             m,
57907             id     = 0,
57908             tpls   = [];
57909     
57910         while(true == !!(m = s.match(re))){
57911             var forMatch   = m[0].match(nameRe),
57912                 ifMatch   = m[0].match(ifRe),
57913                 execMatch   = m[0].match(execRe),
57914                 namedMatch   = m[0].match(namedRe),
57915                 
57916                 exp  = null, 
57917                 fn   = null,
57918                 exec = null,
57919                 name = forMatch && forMatch[1] ? forMatch[1] : '';
57920                 
57921             if (ifMatch) {
57922                 // if - puts fn into test..
57923                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
57924                 if(exp){
57925                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
57926                 }
57927             }
57928             
57929             if (execMatch) {
57930                 // exec - calls a function... returns empty if true is  returned.
57931                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
57932                 if(exp){
57933                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
57934                 }
57935             }
57936             
57937             
57938             if (name) {
57939                 // for = 
57940                 switch(name){
57941                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
57942                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
57943                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
57944                 }
57945             }
57946             var uid = namedMatch ? namedMatch[1] : id;
57947             
57948             
57949             tpls.push({
57950                 id:     namedMatch ? namedMatch[1] : id,
57951                 target: name,
57952                 exec:   exec,
57953                 test:   fn,
57954                 body:   m[1] || ''
57955             });
57956             if (namedMatch) {
57957                 s = s.replace(m[0], '');
57958             } else { 
57959                 s = s.replace(m[0], '{xtpl'+ id + '}');
57960             }
57961             ++id;
57962         }
57963         this.tpls = [];
57964         for(var i = tpls.length-1; i >= 0; --i){
57965             this.compileTpl(tpls[i]);
57966             this.tpls[tpls[i].id] = tpls[i];
57967         }
57968         this.master = tpls[tpls.length-1];
57969         return this;
57970     },
57971     /**
57972      * same as applyTemplate, except it's done to one of the subTemplates
57973      * when using named templates, you can do:
57974      *
57975      * var str = pl.applySubTemplate('your-name', values);
57976      *
57977      * 
57978      * @param {Number} id of the template
57979      * @param {Object} values to apply to template
57980      * @param {Object} parent (normaly the instance of this object)
57981      */
57982     applySubTemplate : function(id, values, parent)
57983     {
57984         
57985         
57986         var t = this.tpls[id];
57987         
57988         
57989         try { 
57990             if(t.test && !t.test.call(this, values, parent)){
57991                 return '';
57992             }
57993         } catch(e) {
57994             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
57995             Roo.log(e.toString());
57996             Roo.log(t.test);
57997             return ''
57998         }
57999         try { 
58000             
58001             if(t.exec && t.exec.call(this, values, parent)){
58002                 return '';
58003             }
58004         } catch(e) {
58005             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58006             Roo.log(e.toString());
58007             Roo.log(t.exec);
58008             return ''
58009         }
58010         try {
58011             var vs = t.target ? t.target.call(this, values, parent) : values;
58012             parent = t.target ? values : parent;
58013             if(t.target && vs instanceof Array){
58014                 var buf = [];
58015                 for(var i = 0, len = vs.length; i < len; i++){
58016                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58017                 }
58018                 return buf.join('');
58019             }
58020             return t.compiled.call(this, vs, parent);
58021         } catch (e) {
58022             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58023             Roo.log(e.toString());
58024             Roo.log(t.compiled);
58025             return '';
58026         }
58027     },
58028
58029     compileTpl : function(tpl)
58030     {
58031         var fm = Roo.util.Format;
58032         var useF = this.disableFormats !== true;
58033         var sep = Roo.isGecko ? "+" : ",";
58034         var undef = function(str) {
58035             Roo.log("Property not found :"  + str);
58036             return '';
58037         };
58038         
58039         var fn = function(m, name, format, args)
58040         {
58041             //Roo.log(arguments);
58042             args = args ? args.replace(/\\'/g,"'") : args;
58043             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58044             if (typeof(format) == 'undefined') {
58045                 format= 'htmlEncode';
58046             }
58047             if (format == 'raw' ) {
58048                 format = false;
58049             }
58050             
58051             if(name.substr(0, 4) == 'xtpl'){
58052                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58053             }
58054             
58055             // build an array of options to determine if value is undefined..
58056             
58057             // basically get 'xxxx.yyyy' then do
58058             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58059             //    (function () { Roo.log("Property not found"); return ''; })() :
58060             //    ......
58061             
58062             var udef_ar = [];
58063             var lookfor = '';
58064             Roo.each(name.split('.'), function(st) {
58065                 lookfor += (lookfor.length ? '.': '') + st;
58066                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58067             });
58068             
58069             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58070             
58071             
58072             if(format && useF){
58073                 
58074                 args = args ? ',' + args : "";
58075                  
58076                 if(format.substr(0, 5) != "this."){
58077                     format = "fm." + format + '(';
58078                 }else{
58079                     format = 'this.call("'+ format.substr(5) + '", ';
58080                     args = ", values";
58081                 }
58082                 
58083                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58084             }
58085              
58086             if (args.length) {
58087                 // called with xxyx.yuu:(test,test)
58088                 // change to ()
58089                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58090             }
58091             // raw.. - :raw modifier..
58092             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58093             
58094         };
58095         var body;
58096         // branched to use + in gecko and [].join() in others
58097         if(Roo.isGecko){
58098             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58099                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58100                     "';};};";
58101         }else{
58102             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58103             body.push(tpl.body.replace(/(\r\n|\n)/g,
58104                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58105             body.push("'].join('');};};");
58106             body = body.join('');
58107         }
58108         
58109         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58110        
58111         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58112         eval(body);
58113         
58114         return this;
58115     },
58116
58117     applyTemplate : function(values){
58118         return this.master.compiled.call(this, values, {});
58119         //var s = this.subs;
58120     },
58121
58122     apply : function(){
58123         return this.applyTemplate.apply(this, arguments);
58124     }
58125
58126  });
58127
58128 Roo.XTemplate.from = function(el){
58129     el = Roo.getDom(el);
58130     return new Roo.XTemplate(el.value || el.innerHTML);
58131 };