roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             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",
650                 "Roo.app", "Roo.ux",
651                 "Roo.bootstrap",
652                 "Roo.bootstrap.dash");
653 /*
654  * Based on:
655  * Ext JS Library 1.1.1
656  * Copyright(c) 2006-2007, Ext JS, LLC.
657  *
658  * Originally Released Under LGPL - original licence link has changed is not relivant.
659  *
660  * Fork - LGPL
661  * <script type="text/javascript">
662  */
663
664 (function() {    
665     // wrappedn so fnCleanup is not in global scope...
666     if(Roo.isIE) {
667         function fnCleanUp() {
668             var p = Function.prototype;
669             delete p.createSequence;
670             delete p.defer;
671             delete p.createDelegate;
672             delete p.createCallback;
673             delete p.createInterceptor;
674
675             window.detachEvent("onunload", fnCleanUp);
676         }
677         window.attachEvent("onunload", fnCleanUp);
678     }
679 })();
680
681
682 /**
683  * @class Function
684  * These functions are available on every Function object (any JavaScript function).
685  */
686 Roo.apply(Function.prototype, {
687      /**
688      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
689      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
690      * Will create a function that is bound to those 2 args.
691      * @return {Function} The new function
692     */
693     createCallback : function(/*args...*/){
694         // make args available, in function below
695         var args = arguments;
696         var method = this;
697         return function() {
698             return method.apply(window, args);
699         };
700     },
701
702     /**
703      * Creates a delegate (callback) that sets the scope to obj.
704      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
705      * Will create a function that is automatically scoped to this.
706      * @param {Object} obj (optional) The object for which the scope is set
707      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
708      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
709      *                                             if a number the args are inserted at the specified position
710      * @return {Function} The new function
711      */
712     createDelegate : function(obj, args, appendArgs){
713         var method = this;
714         return function() {
715             var callArgs = args || arguments;
716             if(appendArgs === true){
717                 callArgs = Array.prototype.slice.call(arguments, 0);
718                 callArgs = callArgs.concat(args);
719             }else if(typeof appendArgs == "number"){
720                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
721                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
722                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
723             }
724             return method.apply(obj || window, callArgs);
725         };
726     },
727
728     /**
729      * Calls this function after the number of millseconds specified.
730      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
731      * @param {Object} obj (optional) The object for which the scope is set
732      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
733      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
734      *                                             if a number the args are inserted at the specified position
735      * @return {Number} The timeout id that can be used with clearTimeout
736      */
737     defer : function(millis, obj, args, appendArgs){
738         var fn = this.createDelegate(obj, args, appendArgs);
739         if(millis){
740             return setTimeout(fn, millis);
741         }
742         fn();
743         return 0;
744     },
745     /**
746      * Create a combined function call sequence of the original function + the passed function.
747      * The resulting function returns the results of the original function.
748      * The passed fcn is called with the parameters of the original function
749      * @param {Function} fcn The function to sequence
750      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
751      * @return {Function} The new function
752      */
753     createSequence : function(fcn, scope){
754         if(typeof fcn != "function"){
755             return this;
756         }
757         var method = this;
758         return function() {
759             var retval = method.apply(this || window, arguments);
760             fcn.apply(scope || this || window, arguments);
761             return retval;
762         };
763     },
764
765     /**
766      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
767      * The resulting function returns the results of the original function.
768      * The passed fcn is called with the parameters of the original function.
769      * @addon
770      * @param {Function} fcn The function to call before the original
771      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
772      * @return {Function} The new function
773      */
774     createInterceptor : function(fcn, scope){
775         if(typeof fcn != "function"){
776             return this;
777         }
778         var method = this;
779         return function() {
780             fcn.target = this;
781             fcn.method = method;
782             if(fcn.apply(scope || this || window, arguments) === false){
783                 return;
784             }
785             return method.apply(this || window, arguments);
786         };
787     }
788 });
789 /*
790  * Based on:
791  * Ext JS Library 1.1.1
792  * Copyright(c) 2006-2007, Ext JS, LLC.
793  *
794  * Originally Released Under LGPL - original licence link has changed is not relivant.
795  *
796  * Fork - LGPL
797  * <script type="text/javascript">
798  */
799
800 Roo.applyIf(String, {
801     
802     /** @scope String */
803     
804     /**
805      * Escapes the passed string for ' and \
806      * @param {String} string The string to escape
807      * @return {String} The escaped string
808      * @static
809      */
810     escape : function(string) {
811         return string.replace(/('|\\)/g, "\\$1");
812     },
813
814     /**
815      * Pads the left side of a string with a specified character.  This is especially useful
816      * for normalizing number and date strings.  Example usage:
817      * <pre><code>
818 var s = String.leftPad('123', 5, '0');
819 // s now contains the string: '00123'
820 </code></pre>
821      * @param {String} string The original string
822      * @param {Number} size The total length of the output string
823      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
824      * @return {String} The padded string
825      * @static
826      */
827     leftPad : function (val, size, ch) {
828         var result = new String(val);
829         if(ch === null || ch === undefined || ch === '') {
830             ch = " ";
831         }
832         while (result.length < size) {
833             result = ch + result;
834         }
835         return result;
836     },
837
838     /**
839      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
840      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
841      * <pre><code>
842 var cls = 'my-class', text = 'Some text';
843 var s = String.format('<div class="{0}">{1}</div>', cls, text);
844 // s now contains the string: '<div class="my-class">Some text</div>'
845 </code></pre>
846      * @param {String} string The tokenized string to be formatted
847      * @param {String} value1 The value to replace token {0}
848      * @param {String} value2 Etc...
849      * @return {String} The formatted string
850      * @static
851      */
852     format : function(format){
853         var args = Array.prototype.slice.call(arguments, 1);
854         return format.replace(/\{(\d+)\}/g, function(m, i){
855             return Roo.util.Format.htmlEncode(args[i]);
856         });
857     }
858 });
859
860 /**
861  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
862  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
863  * they are already different, the first value passed in is returned.  Note that this method returns the new value
864  * but does not change the current string.
865  * <pre><code>
866 // alternate sort directions
867 sort = sort.toggle('ASC', 'DESC');
868
869 // instead of conditional logic:
870 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
871 </code></pre>
872  * @param {String} value The value to compare to the current string
873  * @param {String} other The new value to use if the string already equals the first value passed in
874  * @return {String} The new value
875  */
876  
877 String.prototype.toggle = function(value, other){
878     return this == value ? other : value;
879 };/*
880  * Based on:
881  * Ext JS Library 1.1.1
882  * Copyright(c) 2006-2007, Ext JS, LLC.
883  *
884  * Originally Released Under LGPL - original licence link has changed is not relivant.
885  *
886  * Fork - LGPL
887  * <script type="text/javascript">
888  */
889
890  /**
891  * @class Number
892  */
893 Roo.applyIf(Number.prototype, {
894     /**
895      * Checks whether or not the current number is within a desired range.  If the number is already within the
896      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
897      * exceeded.  Note that this method returns the constrained value but does not change the current number.
898      * @param {Number} min The minimum number in the range
899      * @param {Number} max The maximum number in the range
900      * @return {Number} The constrained value if outside the range, otherwise the current value
901      */
902     constrain : function(min, max){
903         return Math.min(Math.max(this, min), max);
904     }
905 });/*
906  * Based on:
907  * Ext JS Library 1.1.1
908  * Copyright(c) 2006-2007, Ext JS, LLC.
909  *
910  * Originally Released Under LGPL - original licence link has changed is not relivant.
911  *
912  * Fork - LGPL
913  * <script type="text/javascript">
914  */
915  /**
916  * @class Array
917  */
918 Roo.applyIf(Array.prototype, {
919     /**
920      * Checks whether or not the specified object exists in the array.
921      * @param {Object} o The object to check for
922      * @return {Number} The index of o in the array (or -1 if it is not found)
923      */
924     indexOf : function(o){
925        for (var i = 0, len = this.length; i < len; i++){
926               if(this[i] == o) return i;
927        }
928            return -1;
929     },
930
931     /**
932      * Removes the specified object from the array.  If the object is not found nothing happens.
933      * @param {Object} o The object to remove
934      */
935     remove : function(o){
936        var index = this.indexOf(o);
937        if(index != -1){
938            this.splice(index, 1);
939        }
940     },
941     /**
942      * Map (JS 1.6 compatibility)
943      * @param {Function} function  to call
944      */
945     map : function(fun )
946     {
947         var len = this.length >>> 0;
948         if (typeof fun != "function")
949             throw new TypeError();
950
951         var res = new Array(len);
952         var thisp = arguments[1];
953         for (var i = 0; i < len; i++)
954         {
955             if (i in this)
956                 res[i] = fun.call(thisp, this[i], i, this);
957         }
958
959         return res;
960     }
961     
962 });
963
964
965  /*
966  * Based on:
967  * Ext JS Library 1.1.1
968  * Copyright(c) 2006-2007, Ext JS, LLC.
969  *
970  * Originally Released Under LGPL - original licence link has changed is not relivant.
971  *
972  * Fork - LGPL
973  * <script type="text/javascript">
974  */
975
976 /**
977  * @class Date
978  *
979  * The date parsing and format syntax is a subset of
980  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
981  * supported will provide results equivalent to their PHP versions.
982  *
983  * Following is the list of all currently supported formats:
984  *<pre>
985 Sample date:
986 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
987
988 Format  Output      Description
989 ------  ----------  --------------------------------------------------------------
990   d      10         Day of the month, 2 digits with leading zeros
991   D      Wed        A textual representation of a day, three letters
992   j      10         Day of the month without leading zeros
993   l      Wednesday  A full textual representation of the day of the week
994   S      th         English ordinal day of month suffix, 2 chars (use with j)
995   w      3          Numeric representation of the day of the week
996   z      9          The julian date, or day of the year (0-365)
997   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
998   F      January    A full textual representation of the month
999   m      01         Numeric representation of a month, with leading zeros
1000   M      Jan        Month name abbreviation, three letters
1001   n      1          Numeric representation of a month, without leading zeros
1002   t      31         Number of days in the given month
1003   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1004   Y      2007       A full numeric representation of a year, 4 digits
1005   y      07         A two digit representation of a year
1006   a      pm         Lowercase Ante meridiem and Post meridiem
1007   A      PM         Uppercase Ante meridiem and Post meridiem
1008   g      3          12-hour format of an hour without leading zeros
1009   G      15         24-hour format of an hour without leading zeros
1010   h      03         12-hour format of an hour with leading zeros
1011   H      15         24-hour format of an hour with leading zeros
1012   i      05         Minutes with leading zeros
1013   s      01         Seconds, with leading zeros
1014   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1015   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1016   T      CST        Timezone setting of the machine running the code
1017   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1018 </pre>
1019  *
1020  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1021  * <pre><code>
1022 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1023 document.write(dt.format('Y-m-d'));                         //2007-01-10
1024 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1025 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
1026  </code></pre>
1027  *
1028  * Here are some standard date/time patterns that you might find helpful.  They
1029  * are not part of the source of Date.js, but to use them you can simply copy this
1030  * block of code into any script that is included after Date.js and they will also become
1031  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1032  * <pre><code>
1033 Date.patterns = {
1034     ISO8601Long:"Y-m-d H:i:s",
1035     ISO8601Short:"Y-m-d",
1036     ShortDate: "n/j/Y",
1037     LongDate: "l, F d, Y",
1038     FullDateTime: "l, F d, Y g:i:s A",
1039     MonthDay: "F d",
1040     ShortTime: "g:i A",
1041     LongTime: "g:i:s A",
1042     SortableDateTime: "Y-m-d\\TH:i:s",
1043     UniversalSortableDateTime: "Y-m-d H:i:sO",
1044     YearMonth: "F, Y"
1045 };
1046 </code></pre>
1047  *
1048  * Example usage:
1049  * <pre><code>
1050 var dt = new Date();
1051 document.write(dt.format(Date.patterns.ShortDate));
1052  </code></pre>
1053  */
1054
1055 /*
1056  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1057  * They generate precompiled functions from date formats instead of parsing and
1058  * processing the pattern every time you format a date.  These functions are available
1059  * on every Date object (any javascript function).
1060  *
1061  * The original article and download are here:
1062  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1063  *
1064  */
1065  
1066  
1067  // was in core
1068 /**
1069  Returns the number of milliseconds between this date and date
1070  @param {Date} date (optional) Defaults to now
1071  @return {Number} The diff in milliseconds
1072  @member Date getElapsed
1073  */
1074 Date.prototype.getElapsed = function(date) {
1075         return Math.abs((date || new Date()).getTime()-this.getTime());
1076 };
1077 // was in date file..
1078
1079
1080 // private
1081 Date.parseFunctions = {count:0};
1082 // private
1083 Date.parseRegexes = [];
1084 // private
1085 Date.formatFunctions = {count:0};
1086
1087 // private
1088 Date.prototype.dateFormat = function(format) {
1089     if (Date.formatFunctions[format] == null) {
1090         Date.createNewFormat(format);
1091     }
1092     var func = Date.formatFunctions[format];
1093     return this[func]();
1094 };
1095
1096
1097 /**
1098  * Formats a date given the supplied format string
1099  * @param {String} format The format string
1100  * @return {String} The formatted date
1101  * @method
1102  */
1103 Date.prototype.format = Date.prototype.dateFormat;
1104
1105 // private
1106 Date.createNewFormat = function(format) {
1107     var funcName = "format" + Date.formatFunctions.count++;
1108     Date.formatFunctions[format] = funcName;
1109     var code = "Date.prototype." + funcName + " = function(){return ";
1110     var special = false;
1111     var ch = '';
1112     for (var i = 0; i < format.length; ++i) {
1113         ch = format.charAt(i);
1114         if (!special && ch == "\\") {
1115             special = true;
1116         }
1117         else if (special) {
1118             special = false;
1119             code += "'" + String.escape(ch) + "' + ";
1120         }
1121         else {
1122             code += Date.getFormatCode(ch);
1123         }
1124     }
1125     /** eval:var:zzzzzzzzzzzzz */
1126     eval(code.substring(0, code.length - 3) + ";}");
1127 };
1128
1129 // private
1130 Date.getFormatCode = function(character) {
1131     switch (character) {
1132     case "d":
1133         return "String.leftPad(this.getDate(), 2, '0') + ";
1134     case "D":
1135         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1136     case "j":
1137         return "this.getDate() + ";
1138     case "l":
1139         return "Date.dayNames[this.getDay()] + ";
1140     case "S":
1141         return "this.getSuffix() + ";
1142     case "w":
1143         return "this.getDay() + ";
1144     case "z":
1145         return "this.getDayOfYear() + ";
1146     case "W":
1147         return "this.getWeekOfYear() + ";
1148     case "F":
1149         return "Date.monthNames[this.getMonth()] + ";
1150     case "m":
1151         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1152     case "M":
1153         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1154     case "n":
1155         return "(this.getMonth() + 1) + ";
1156     case "t":
1157         return "this.getDaysInMonth() + ";
1158     case "L":
1159         return "(this.isLeapYear() ? 1 : 0) + ";
1160     case "Y":
1161         return "this.getFullYear() + ";
1162     case "y":
1163         return "('' + this.getFullYear()).substring(2, 4) + ";
1164     case "a":
1165         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1166     case "A":
1167         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1168     case "g":
1169         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1170     case "G":
1171         return "this.getHours() + ";
1172     case "h":
1173         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1174     case "H":
1175         return "String.leftPad(this.getHours(), 2, '0') + ";
1176     case "i":
1177         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1178     case "s":
1179         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1180     case "O":
1181         return "this.getGMTOffset() + ";
1182     case "P":
1183         return "this.getGMTColonOffset() + ";
1184     case "T":
1185         return "this.getTimezone() + ";
1186     case "Z":
1187         return "(this.getTimezoneOffset() * -60) + ";
1188     default:
1189         return "'" + String.escape(character) + "' + ";
1190     }
1191 };
1192
1193 /**
1194  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1195  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1196  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1197  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1198  * string or the parse operation will fail.
1199  * Example Usage:
1200 <pre><code>
1201 //dt = Fri May 25 2007 (current date)
1202 var dt = new Date();
1203
1204 //dt = Thu May 25 2006 (today's month/day in 2006)
1205 dt = Date.parseDate("2006", "Y");
1206
1207 //dt = Sun Jan 15 2006 (all date parts specified)
1208 dt = Date.parseDate("2006-1-15", "Y-m-d");
1209
1210 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1211 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1212 </code></pre>
1213  * @param {String} input The unparsed date as a string
1214  * @param {String} format The format the date is in
1215  * @return {Date} The parsed date
1216  * @static
1217  */
1218 Date.parseDate = function(input, format) {
1219     if (Date.parseFunctions[format] == null) {
1220         Date.createParser(format);
1221     }
1222     var func = Date.parseFunctions[format];
1223     return Date[func](input);
1224 };
1225 /**
1226  * @private
1227  */
1228 Date.createParser = function(format) {
1229     var funcName = "parse" + Date.parseFunctions.count++;
1230     var regexNum = Date.parseRegexes.length;
1231     var currentGroup = 1;
1232     Date.parseFunctions[format] = funcName;
1233
1234     var code = "Date." + funcName + " = function(input){\n"
1235         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1236         + "var d = new Date();\n"
1237         + "y = d.getFullYear();\n"
1238         + "m = d.getMonth();\n"
1239         + "d = d.getDate();\n"
1240         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1241         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1242         + "if (results && results.length > 0) {";
1243     var regex = "";
1244
1245     var special = false;
1246     var ch = '';
1247     for (var i = 0; i < format.length; ++i) {
1248         ch = format.charAt(i);
1249         if (!special && ch == "\\") {
1250             special = true;
1251         }
1252         else if (special) {
1253             special = false;
1254             regex += String.escape(ch);
1255         }
1256         else {
1257             var obj = Date.formatCodeToRegex(ch, currentGroup);
1258             currentGroup += obj.g;
1259             regex += obj.s;
1260             if (obj.g && obj.c) {
1261                 code += obj.c;
1262             }
1263         }
1264     }
1265
1266     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1267         + "{v = new Date(y, m, d, h, i, s);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1269         + "{v = new Date(y, m, d, h, i);}\n"
1270         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1271         + "{v = new Date(y, m, d, h);}\n"
1272         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1273         + "{v = new Date(y, m, d);}\n"
1274         + "else if (y >= 0 && m >= 0)\n"
1275         + "{v = new Date(y, m);}\n"
1276         + "else if (y >= 0)\n"
1277         + "{v = new Date(y);}\n"
1278         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1279         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1280         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1281         + ";}";
1282
1283     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1284     /** eval:var:zzzzzzzzzzzzz */
1285     eval(code);
1286 };
1287
1288 // private
1289 Date.formatCodeToRegex = function(character, currentGroup) {
1290     switch (character) {
1291     case "D":
1292         return {g:0,
1293         c:null,
1294         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1295     case "j":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{1,2})"}; // day of month without leading zeroes
1299     case "d":
1300         return {g:1,
1301             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1302             s:"(\\d{2})"}; // day of month with leading zeroes
1303     case "l":
1304         return {g:0,
1305             c:null,
1306             s:"(?:" + Date.dayNames.join("|") + ")"};
1307     case "S":
1308         return {g:0,
1309             c:null,
1310             s:"(?:st|nd|rd|th)"};
1311     case "w":
1312         return {g:0,
1313             c:null,
1314             s:"\\d"};
1315     case "z":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{1,3})"};
1319     case "W":
1320         return {g:0,
1321             c:null,
1322             s:"(?:\\d{2})"};
1323     case "F":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1326             s:"(" + Date.monthNames.join("|") + ")"};
1327     case "M":
1328         return {g:1,
1329             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1330             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1331     case "n":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1335     case "m":
1336         return {g:1,
1337             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1338             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1339     case "t":
1340         return {g:0,
1341             c:null,
1342             s:"\\d{1,2}"};
1343     case "L":
1344         return {g:0,
1345             c:null,
1346             s:"(?:1|0)"};
1347     case "Y":
1348         return {g:1,
1349             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1350             s:"(\\d{4})"};
1351     case "y":
1352         return {g:1,
1353             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1354                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1355             s:"(\\d{1,2})"};
1356     case "a":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'am') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(am|pm)"};
1362     case "A":
1363         return {g:1,
1364             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1365                 + "if (h == 12) { h = 0; }\n"
1366                 + "} else { if (h < 12) { h += 12; }}",
1367             s:"(AM|PM)"};
1368     case "g":
1369     case "G":
1370         return {g:1,
1371             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1373     case "h":
1374     case "H":
1375         return {g:1,
1376             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1378     case "i":
1379         return {g:1,
1380             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "s":
1383         return {g:1,
1384             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1385             s:"(\\d{2})"};
1386     case "O":
1387         return {g:1,
1388             c:[
1389                 "o = results[", currentGroup, "];\n",
1390                 "var sn = o.substring(0,1);\n", // get + / - sign
1391                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1392                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1393                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1394                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1395             ].join(""),
1396             s:"([+\-]\\d{2,4})"};
1397     
1398     
1399     case "P":
1400         return {g:1,
1401                 c:[
1402                    "o = results[", currentGroup, "];\n",
1403                    "var sn = o.substring(0,1);\n",
1404                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1405                    "var mn = o.substring(4,6) % 60;\n",
1406                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1407                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1408             ].join(""),
1409             s:"([+\-]\\d{4})"};
1410     case "T":
1411         return {g:0,
1412             c:null,
1413             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1414     case "Z":
1415         return {g:1,
1416             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1417                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1418             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1419     default:
1420         return {g:0,
1421             c:null,
1422             s:String.escape(character)};
1423     }
1424 };
1425
1426 /**
1427  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1428  * @return {String} The abbreviated timezone name (e.g. 'CST')
1429  */
1430 Date.prototype.getTimezone = function() {
1431     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1432 };
1433
1434 /**
1435  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1436  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1437  */
1438 Date.prototype.getGMTOffset = function() {
1439     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1440         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1441         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1442 };
1443
1444 /**
1445  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1446  * @return {String} 2-characters representing hours and 2-characters representing minutes
1447  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1448  */
1449 Date.prototype.getGMTColonOffset = function() {
1450         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1451                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1452                 + ":"
1453                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1454 }
1455
1456 /**
1457  * Get the numeric day number of the year, adjusted for leap year.
1458  * @return {Number} 0 through 364 (365 in leap years)
1459  */
1460 Date.prototype.getDayOfYear = function() {
1461     var num = 0;
1462     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1463     for (var i = 0; i < this.getMonth(); ++i) {
1464         num += Date.daysInMonth[i];
1465     }
1466     return num + this.getDate() - 1;
1467 };
1468
1469 /**
1470  * Get the string representation of the numeric week number of the year
1471  * (equivalent to the format specifier 'W').
1472  * @return {String} '00' through '52'
1473  */
1474 Date.prototype.getWeekOfYear = function() {
1475     // Skip to Thursday of this week
1476     var now = this.getDayOfYear() + (4 - this.getDay());
1477     // Find the first Thursday of the year
1478     var jan1 = new Date(this.getFullYear(), 0, 1);
1479     var then = (7 - jan1.getDay() + 4);
1480     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1481 };
1482
1483 /**
1484  * Whether or not the current date is in a leap year.
1485  * @return {Boolean} True if the current date is in a leap year, else false
1486  */
1487 Date.prototype.isLeapYear = function() {
1488     var year = this.getFullYear();
1489     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1490 };
1491
1492 /**
1493  * Get the first day of the current month, adjusted for leap year.  The returned value
1494  * is the numeric day index within the week (0-6) which can be used in conjunction with
1495  * the {@link #monthNames} array to retrieve the textual day name.
1496  * Example:
1497  *<pre><code>
1498 var dt = new Date('1/10/2007');
1499 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1500 </code></pre>
1501  * @return {Number} The day number (0-6)
1502  */
1503 Date.prototype.getFirstDayOfMonth = function() {
1504     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1505     return (day < 0) ? (day + 7) : day;
1506 };
1507
1508 /**
1509  * Get the last day of the current month, adjusted for leap year.  The returned value
1510  * is the numeric day index within the week (0-6) which can be used in conjunction with
1511  * the {@link #monthNames} array to retrieve the textual day name.
1512  * Example:
1513  *<pre><code>
1514 var dt = new Date('1/10/2007');
1515 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1516 </code></pre>
1517  * @return {Number} The day number (0-6)
1518  */
1519 Date.prototype.getLastDayOfMonth = function() {
1520     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1521     return (day < 0) ? (day + 7) : day;
1522 };
1523
1524
1525 /**
1526  * Get the first date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getFirstDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), 1);
1531 };
1532
1533 /**
1534  * Get the last date of this date's month
1535  * @return {Date}
1536  */
1537 Date.prototype.getLastDateOfMonth = function() {
1538     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1539 };
1540 /**
1541  * Get the number of days in the current month, adjusted for leap year.
1542  * @return {Number} The number of days in the month
1543  */
1544 Date.prototype.getDaysInMonth = function() {
1545     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1546     return Date.daysInMonth[this.getMonth()];
1547 };
1548
1549 /**
1550  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1551  * @return {String} 'st, 'nd', 'rd' or 'th'
1552  */
1553 Date.prototype.getSuffix = function() {
1554     switch (this.getDate()) {
1555         case 1:
1556         case 21:
1557         case 31:
1558             return "st";
1559         case 2:
1560         case 22:
1561             return "nd";
1562         case 3:
1563         case 23:
1564             return "rd";
1565         default:
1566             return "th";
1567     }
1568 };
1569
1570 // private
1571 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1572
1573 /**
1574  * An array of textual month names.
1575  * Override these values for international dates, for example...
1576  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1577  * @type Array
1578  * @static
1579  */
1580 Date.monthNames =
1581    ["January",
1582     "February",
1583     "March",
1584     "April",
1585     "May",
1586     "June",
1587     "July",
1588     "August",
1589     "September",
1590     "October",
1591     "November",
1592     "December"];
1593
1594 /**
1595  * An array of textual day names.
1596  * Override these values for international dates, for example...
1597  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1598  * @type Array
1599  * @static
1600  */
1601 Date.dayNames =
1602    ["Sunday",
1603     "Monday",
1604     "Tuesday",
1605     "Wednesday",
1606     "Thursday",
1607     "Friday",
1608     "Saturday"];
1609
1610 // private
1611 Date.y2kYear = 50;
1612 // private
1613 Date.monthNumbers = {
1614     Jan:0,
1615     Feb:1,
1616     Mar:2,
1617     Apr:3,
1618     May:4,
1619     Jun:5,
1620     Jul:6,
1621     Aug:7,
1622     Sep:8,
1623     Oct:9,
1624     Nov:10,
1625     Dec:11};
1626
1627 /**
1628  * Creates and returns a new Date instance with the exact same date value as the called instance.
1629  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1630  * variable will also be changed.  When the intention is to create a new variable that will not
1631  * modify the original instance, you should create a clone.
1632  *
1633  * Example of correctly cloning a date:
1634  * <pre><code>
1635 //wrong way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig;
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 05 2006'!
1640
1641 //correct way:
1642 var orig = new Date('10/1/2006');
1643 var copy = orig.clone();
1644 copy.setDate(5);
1645 document.write(orig);  //returns 'Thu Oct 01 2006'
1646 </code></pre>
1647  * @return {Date} The new Date instance
1648  */
1649 Date.prototype.clone = function() {
1650         return new Date(this.getTime());
1651 };
1652
1653 /**
1654  * Clears any time information from this date
1655  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1656  @return {Date} this or the clone
1657  */
1658 Date.prototype.clearTime = function(clone){
1659     if(clone){
1660         return this.clone().clearTime();
1661     }
1662     this.setHours(0);
1663     this.setMinutes(0);
1664     this.setSeconds(0);
1665     this.setMilliseconds(0);
1666     return this;
1667 };
1668
1669 // private
1670 // safari setMonth is broken
1671 if(Roo.isSafari){
1672     Date.brokenSetMonth = Date.prototype.setMonth;
1673         Date.prototype.setMonth = function(num){
1674                 if(num <= -1){
1675                         var n = Math.ceil(-num);
1676                         var back_year = Math.ceil(n/12);
1677                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1678                         this.setFullYear(this.getFullYear() - back_year);
1679                         return Date.brokenSetMonth.call(this, month);
1680                 } else {
1681                         return Date.brokenSetMonth.apply(this, arguments);
1682                 }
1683         };
1684 }
1685
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MILLI = "ms";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.SECOND = "s";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.MINUTE = "mi";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.HOUR = "h";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.DAY = "d";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.MONTH = "mo";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.YEAR = "y";
1714
1715 /**
1716  * Provides a convenient method of performing basic date arithmetic.  This method
1717  * does not modify the Date instance being called - it creates and returns
1718  * a new Date instance containing the resulting date value.
1719  *
1720  * Examples:
1721  * <pre><code>
1722 //Basic usage:
1723 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1724 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1725
1726 //Negative values will subtract correctly:
1727 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1728 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1729
1730 //You can even chain several calls together in one line!
1731 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1732 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1733  </code></pre>
1734  *
1735  * @param {String} interval   A valid date interval enum value
1736  * @param {Number} value      The amount to add to the current date
1737  * @return {Date} The new Date instance
1738  */
1739 Date.prototype.add = function(interval, value){
1740   var d = this.clone();
1741   if (!interval || value === 0) return d;
1742   switch(interval.toLowerCase()){
1743     case Date.MILLI:
1744       d.setMilliseconds(this.getMilliseconds() + value);
1745       break;
1746     case Date.SECOND:
1747       d.setSeconds(this.getSeconds() + value);
1748       break;
1749     case Date.MINUTE:
1750       d.setMinutes(this.getMinutes() + value);
1751       break;
1752     case Date.HOUR:
1753       d.setHours(this.getHours() + value);
1754       break;
1755     case Date.DAY:
1756       d.setDate(this.getDate() + value);
1757       break;
1758     case Date.MONTH:
1759       var day = this.getDate();
1760       if(day > 28){
1761           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1762       }
1763       d.setDate(day);
1764       d.setMonth(this.getMonth() + value);
1765       break;
1766     case Date.YEAR:
1767       d.setFullYear(this.getFullYear() + value);
1768       break;
1769   }
1770   return d;
1771 };
1772 /*
1773  * Based on:
1774  * Ext JS Library 1.1.1
1775  * Copyright(c) 2006-2007, Ext JS, LLC.
1776  *
1777  * Originally Released Under LGPL - original licence link has changed is not relivant.
1778  *
1779  * Fork - LGPL
1780  * <script type="text/javascript">
1781  */
1782
1783 /**
1784  * @class Roo.lib.Dom
1785  * @static
1786  * 
1787  * Dom utils (from YIU afaik)
1788  * 
1789  **/
1790 Roo.lib.Dom = {
1791     /**
1792      * Get the view width
1793      * @param {Boolean} full True will get the full document, otherwise it's the view width
1794      * @return {Number} The width
1795      */
1796      
1797     getViewWidth : function(full) {
1798         return full ? this.getDocumentWidth() : this.getViewportWidth();
1799     },
1800     /**
1801      * Get the view height
1802      * @param {Boolean} full True will get the full document, otherwise it's the view height
1803      * @return {Number} The height
1804      */
1805     getViewHeight : function(full) {
1806         return full ? this.getDocumentHeight() : this.getViewportHeight();
1807     },
1808
1809     getDocumentHeight: function() {
1810         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1811         return Math.max(scrollHeight, this.getViewportHeight());
1812     },
1813
1814     getDocumentWidth: function() {
1815         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1816         return Math.max(scrollWidth, this.getViewportWidth());
1817     },
1818
1819     getViewportHeight: function() {
1820         var height = self.innerHeight;
1821         var mode = document.compatMode;
1822
1823         if ((mode || Roo.isIE) && !Roo.isOpera) {
1824             height = (mode == "CSS1Compat") ?
1825                      document.documentElement.clientHeight :
1826                      document.body.clientHeight;
1827         }
1828
1829         return height;
1830     },
1831
1832     getViewportWidth: function() {
1833         var width = self.innerWidth;
1834         var mode = document.compatMode;
1835
1836         if (mode || Roo.isIE) {
1837             width = (mode == "CSS1Compat") ?
1838                     document.documentElement.clientWidth :
1839                     document.body.clientWidth;
1840         }
1841         return width;
1842     },
1843
1844     isAncestor : function(p, c) {
1845         p = Roo.getDom(p);
1846         c = Roo.getDom(c);
1847         if (!p || !c) {
1848             return false;
1849         }
1850
1851         if (p.contains && !Roo.isSafari) {
1852             return p.contains(c);
1853         } else if (p.compareDocumentPosition) {
1854             return !!(p.compareDocumentPosition(c) & 16);
1855         } else {
1856             var parent = c.parentNode;
1857             while (parent) {
1858                 if (parent == p) {
1859                     return true;
1860                 }
1861                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1862                     return false;
1863                 }
1864                 parent = parent.parentNode;
1865             }
1866             return false;
1867         }
1868     },
1869
1870     getRegion : function(el) {
1871         return Roo.lib.Region.getRegion(el);
1872     },
1873
1874     getY : function(el) {
1875         return this.getXY(el)[1];
1876     },
1877
1878     getX : function(el) {
1879         return this.getXY(el)[0];
1880     },
1881
1882     getXY : function(el) {
1883         var p, pe, b, scroll, bd = document.body;
1884         el = Roo.getDom(el);
1885         var fly = Roo.lib.AnimBase.fly;
1886         if (el.getBoundingClientRect) {
1887             b = el.getBoundingClientRect();
1888             scroll = fly(document).getScroll();
1889             return [b.left + scroll.left, b.top + scroll.top];
1890         }
1891         var x = 0, y = 0;
1892
1893         p = el;
1894
1895         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1896
1897         while (p) {
1898
1899             x += p.offsetLeft;
1900             y += p.offsetTop;
1901
1902             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1903                 hasAbsolute = true;
1904             }
1905
1906             if (Roo.isGecko) {
1907                 pe = fly(p);
1908
1909                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1910                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1911
1912
1913                 x += bl;
1914                 y += bt;
1915
1916
1917                 if (p != el && pe.getStyle('overflow') != 'visible') {
1918                     x += bl;
1919                     y += bt;
1920                 }
1921             }
1922             p = p.offsetParent;
1923         }
1924
1925         if (Roo.isSafari && hasAbsolute) {
1926             x -= bd.offsetLeft;
1927             y -= bd.offsetTop;
1928         }
1929
1930         if (Roo.isGecko && !hasAbsolute) {
1931             var dbd = fly(bd);
1932             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1933             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1934         }
1935
1936         p = el.parentNode;
1937         while (p && p != bd) {
1938             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1939                 x -= p.scrollLeft;
1940                 y -= p.scrollTop;
1941             }
1942             p = p.parentNode;
1943         }
1944         return [x, y];
1945     },
1946  
1947   
1948
1949
1950     setXY : function(el, xy) {
1951         el = Roo.fly(el, '_setXY');
1952         el.position();
1953         var pts = el.translatePoints(xy);
1954         if (xy[0] !== false) {
1955             el.dom.style.left = pts.left + "px";
1956         }
1957         if (xy[1] !== false) {
1958             el.dom.style.top = pts.top + "px";
1959         }
1960     },
1961
1962     setX : function(el, x) {
1963         this.setXY(el, [x, false]);
1964     },
1965
1966     setY : function(el, y) {
1967         this.setXY(el, [false, y]);
1968     }
1969 };
1970 /*
1971  * Portions of this file are based on pieces of Yahoo User Interface Library
1972  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1973  * YUI licensed under the BSD License:
1974  * http://developer.yahoo.net/yui/license.txt
1975  * <script type="text/javascript">
1976  *
1977  */
1978
1979 Roo.lib.Event = function() {
1980     var loadComplete = false;
1981     var listeners = [];
1982     var unloadListeners = [];
1983     var retryCount = 0;
1984     var onAvailStack = [];
1985     var counter = 0;
1986     var lastError = null;
1987
1988     return {
1989         POLL_RETRYS: 200,
1990         POLL_INTERVAL: 20,
1991         EL: 0,
1992         TYPE: 1,
1993         FN: 2,
1994         WFN: 3,
1995         OBJ: 3,
1996         ADJ_SCOPE: 4,
1997         _interval: null,
1998
1999         startInterval: function() {
2000             if (!this._interval) {
2001                 var self = this;
2002                 var callback = function() {
2003                     self._tryPreloadAttach();
2004                 };
2005                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2006
2007             }
2008         },
2009
2010         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2011             onAvailStack.push({ id:         p_id,
2012                 fn:         p_fn,
2013                 obj:        p_obj,
2014                 override:   p_override,
2015                 checkReady: false    });
2016
2017             retryCount = this.POLL_RETRYS;
2018             this.startInterval();
2019         },
2020
2021
2022         addListener: function(el, eventName, fn) {
2023             el = Roo.getDom(el);
2024             if (!el || !fn) {
2025                 return false;
2026             }
2027
2028             if ("unload" == eventName) {
2029                 unloadListeners[unloadListeners.length] =
2030                 [el, eventName, fn];
2031                 return true;
2032             }
2033
2034             var wrappedFn = function(e) {
2035                 return fn(Roo.lib.Event.getEvent(e));
2036             };
2037
2038             var li = [el, eventName, fn, wrappedFn];
2039
2040             var index = listeners.length;
2041             listeners[index] = li;
2042
2043             this.doAdd(el, eventName, wrappedFn, false);
2044             return true;
2045
2046         },
2047
2048
2049         removeListener: function(el, eventName, fn) {
2050             var i, len;
2051
2052             el = Roo.getDom(el);
2053
2054             if(!fn) {
2055                 return this.purgeElement(el, false, eventName);
2056             }
2057
2058
2059             if ("unload" == eventName) {
2060
2061                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2062                     var li = unloadListeners[i];
2063                     if (li &&
2064                         li[0] == el &&
2065                         li[1] == eventName &&
2066                         li[2] == fn) {
2067                         unloadListeners.splice(i, 1);
2068                         return true;
2069                     }
2070                 }
2071
2072                 return false;
2073             }
2074
2075             var cacheItem = null;
2076
2077
2078             var index = arguments[3];
2079
2080             if ("undefined" == typeof index) {
2081                 index = this._getCacheIndex(el, eventName, fn);
2082             }
2083
2084             if (index >= 0) {
2085                 cacheItem = listeners[index];
2086             }
2087
2088             if (!el || !cacheItem) {
2089                 return false;
2090             }
2091
2092             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2093
2094             delete listeners[index][this.WFN];
2095             delete listeners[index][this.FN];
2096             listeners.splice(index, 1);
2097
2098             return true;
2099
2100         },
2101
2102
2103         getTarget: function(ev, resolveTextNode) {
2104             ev = ev.browserEvent || ev;
2105             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2106             var t = ev.target || ev.srcElement;
2107             return this.resolveTextNode(t);
2108         },
2109
2110
2111         resolveTextNode: function(node) {
2112             if (Roo.isSafari && node && 3 == node.nodeType) {
2113                 return node.parentNode;
2114             } else {
2115                 return node;
2116             }
2117         },
2118
2119
2120         getPageX: function(ev) {
2121             ev = ev.browserEvent || ev;
2122             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2123             var x = ev.pageX;
2124             if (!x && 0 !== x) {
2125                 x = ev.clientX || 0;
2126
2127                 if (Roo.isIE) {
2128                     x += this.getScroll()[1];
2129                 }
2130             }
2131
2132             return x;
2133         },
2134
2135
2136         getPageY: function(ev) {
2137             ev = ev.browserEvent || ev;
2138             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2139             var y = ev.pageY;
2140             if (!y && 0 !== y) {
2141                 y = ev.clientY || 0;
2142
2143                 if (Roo.isIE) {
2144                     y += this.getScroll()[0];
2145                 }
2146             }
2147
2148
2149             return y;
2150         },
2151
2152
2153         getXY: function(ev) {
2154             ev = ev.browserEvent || ev;
2155             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2156             return [this.getPageX(ev), this.getPageY(ev)];
2157         },
2158
2159
2160         getRelatedTarget: function(ev) {
2161             ev = ev.browserEvent || ev;
2162             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2163             var t = ev.relatedTarget;
2164             if (!t) {
2165                 if (ev.type == "mouseout") {
2166                     t = ev.toElement;
2167                 } else if (ev.type == "mouseover") {
2168                     t = ev.fromElement;
2169                 }
2170             }
2171
2172             return this.resolveTextNode(t);
2173         },
2174
2175
2176         getTime: function(ev) {
2177             ev = ev.browserEvent || ev;
2178             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2179             if (!ev.time) {
2180                 var t = new Date().getTime();
2181                 try {
2182                     ev.time = t;
2183                 } catch(ex) {
2184                     this.lastError = ex;
2185                     return t;
2186                 }
2187             }
2188
2189             return ev.time;
2190         },
2191
2192
2193         stopEvent: function(ev) {
2194             this.stopPropagation(ev);
2195             this.preventDefault(ev);
2196         },
2197
2198
2199         stopPropagation: function(ev) {
2200             ev = ev.browserEvent || ev;
2201             if (ev.stopPropagation) {
2202                 ev.stopPropagation();
2203             } else {
2204                 ev.cancelBubble = true;
2205             }
2206         },
2207
2208
2209         preventDefault: function(ev) {
2210             ev = ev.browserEvent || ev;
2211             if(ev.preventDefault) {
2212                 ev.preventDefault();
2213             } else {
2214                 ev.returnValue = false;
2215             }
2216         },
2217
2218
2219         getEvent: function(e) {
2220             var ev = e || window.event;
2221             if (!ev) {
2222                 var c = this.getEvent.caller;
2223                 while (c) {
2224                     ev = c.arguments[0];
2225                     if (ev && Event == ev.constructor) {
2226                         break;
2227                     }
2228                     c = c.caller;
2229                 }
2230             }
2231             return ev;
2232         },
2233
2234
2235         getCharCode: function(ev) {
2236             ev = ev.browserEvent || ev;
2237             return ev.charCode || ev.keyCode || 0;
2238         },
2239
2240
2241         _getCacheIndex: function(el, eventName, fn) {
2242             for (var i = 0,len = listeners.length; i < len; ++i) {
2243                 var li = listeners[i];
2244                 if (li &&
2245                     li[this.FN] == fn &&
2246                     li[this.EL] == el &&
2247                     li[this.TYPE] == eventName) {
2248                     return i;
2249                 }
2250             }
2251
2252             return -1;
2253         },
2254
2255
2256         elCache: {},
2257
2258
2259         getEl: function(id) {
2260             return document.getElementById(id);
2261         },
2262
2263
2264         clearCache: function() {
2265         },
2266
2267
2268         _load: function(e) {
2269             loadComplete = true;
2270             var EU = Roo.lib.Event;
2271
2272
2273             if (Roo.isIE) {
2274                 EU.doRemove(window, "load", EU._load);
2275             }
2276         },
2277
2278
2279         _tryPreloadAttach: function() {
2280
2281             if (this.locked) {
2282                 return false;
2283             }
2284
2285             this.locked = true;
2286
2287
2288             var tryAgain = !loadComplete;
2289             if (!tryAgain) {
2290                 tryAgain = (retryCount > 0);
2291             }
2292
2293
2294             var notAvail = [];
2295             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2296                 var item = onAvailStack[i];
2297                 if (item) {
2298                     var el = this.getEl(item.id);
2299
2300                     if (el) {
2301                         if (!item.checkReady ||
2302                             loadComplete ||
2303                             el.nextSibling ||
2304                             (document && document.body)) {
2305
2306                             var scope = el;
2307                             if (item.override) {
2308                                 if (item.override === true) {
2309                                     scope = item.obj;
2310                                 } else {
2311                                     scope = item.override;
2312                                 }
2313                             }
2314                             item.fn.call(scope, item.obj);
2315                             onAvailStack[i] = null;
2316                         }
2317                     } else {
2318                         notAvail.push(item);
2319                     }
2320                 }
2321             }
2322
2323             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2324
2325             if (tryAgain) {
2326
2327                 this.startInterval();
2328             } else {
2329                 clearInterval(this._interval);
2330                 this._interval = null;
2331             }
2332
2333             this.locked = false;
2334
2335             return true;
2336
2337         },
2338
2339
2340         purgeElement: function(el, recurse, eventName) {
2341             var elListeners = this.getListeners(el, eventName);
2342             if (elListeners) {
2343                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2344                     var l = elListeners[i];
2345                     this.removeListener(el, l.type, l.fn);
2346                 }
2347             }
2348
2349             if (recurse && el && el.childNodes) {
2350                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2351                     this.purgeElement(el.childNodes[i], recurse, eventName);
2352                 }
2353             }
2354         },
2355
2356
2357         getListeners: function(el, eventName) {
2358             var results = [], searchLists;
2359             if (!eventName) {
2360                 searchLists = [listeners, unloadListeners];
2361             } else if (eventName == "unload") {
2362                 searchLists = [unloadListeners];
2363             } else {
2364                 searchLists = [listeners];
2365             }
2366
2367             for (var j = 0; j < searchLists.length; ++j) {
2368                 var searchList = searchLists[j];
2369                 if (searchList && searchList.length > 0) {
2370                     for (var i = 0,len = searchList.length; i < len; ++i) {
2371                         var l = searchList[i];
2372                         if (l && l[this.EL] === el &&
2373                             (!eventName || eventName === l[this.TYPE])) {
2374                             results.push({
2375                                 type:   l[this.TYPE],
2376                                 fn:     l[this.FN],
2377                                 obj:    l[this.OBJ],
2378                                 adjust: l[this.ADJ_SCOPE],
2379                                 index:  i
2380                             });
2381                         }
2382                     }
2383                 }
2384             }
2385
2386             return (results.length) ? results : null;
2387         },
2388
2389
2390         _unload: function(e) {
2391
2392             var EU = Roo.lib.Event, i, j, l, len, index;
2393
2394             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2395                 l = unloadListeners[i];
2396                 if (l) {
2397                     var scope = window;
2398                     if (l[EU.ADJ_SCOPE]) {
2399                         if (l[EU.ADJ_SCOPE] === true) {
2400                             scope = l[EU.OBJ];
2401                         } else {
2402                             scope = l[EU.ADJ_SCOPE];
2403                         }
2404                     }
2405                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2406                     unloadListeners[i] = null;
2407                     l = null;
2408                     scope = null;
2409                 }
2410             }
2411
2412             unloadListeners = null;
2413
2414             if (listeners && listeners.length > 0) {
2415                 j = listeners.length;
2416                 while (j) {
2417                     index = j - 1;
2418                     l = listeners[index];
2419                     if (l) {
2420                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2421                                 l[EU.FN], index);
2422                     }
2423                     j = j - 1;
2424                 }
2425                 l = null;
2426
2427                 EU.clearCache();
2428             }
2429
2430             EU.doRemove(window, "unload", EU._unload);
2431
2432         },
2433
2434
2435         getScroll: function() {
2436             var dd = document.documentElement, db = document.body;
2437             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2438                 return [dd.scrollTop, dd.scrollLeft];
2439             } else if (db) {
2440                 return [db.scrollTop, db.scrollLeft];
2441             } else {
2442                 return [0, 0];
2443             }
2444         },
2445
2446
2447         doAdd: function () {
2448             if (window.addEventListener) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.addEventListener(eventName, fn, (capture));
2451                 };
2452             } else if (window.attachEvent) {
2453                 return function(el, eventName, fn, capture) {
2454                     el.attachEvent("on" + eventName, fn);
2455                 };
2456             } else {
2457                 return function() {
2458                 };
2459             }
2460         }(),
2461
2462
2463         doRemove: function() {
2464             if (window.removeEventListener) {
2465                 return function (el, eventName, fn, capture) {
2466                     el.removeEventListener(eventName, fn, (capture));
2467                 };
2468             } else if (window.detachEvent) {
2469                 return function (el, eventName, fn) {
2470                     el.detachEvent("on" + eventName, fn);
2471                 };
2472             } else {
2473                 return function() {
2474                 };
2475             }
2476         }()
2477     };
2478     
2479 }();
2480 (function() {     
2481    
2482     var E = Roo.lib.Event;
2483     E.on = E.addListener;
2484     E.un = E.removeListener;
2485
2486     if (document && document.body) {
2487         E._load();
2488     } else {
2489         E.doAdd(window, "load", E._load);
2490     }
2491     E.doAdd(window, "unload", E._unload);
2492     E._tryPreloadAttach();
2493 })();
2494
2495 /*
2496  * Portions of this file are based on pieces of Yahoo User Interface Library
2497  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2498  * YUI licensed under the BSD License:
2499  * http://developer.yahoo.net/yui/license.txt
2500  * <script type="text/javascript">
2501  *
2502  */
2503
2504 (function() {
2505     /**
2506      * @class Roo.lib.Ajax
2507      *
2508      */
2509     Roo.lib.Ajax = {
2510         /**
2511          * @static 
2512          */
2513         request : function(method, uri, cb, data, options) {
2514             if(options){
2515                 var hs = options.headers;
2516                 if(hs){
2517                     for(var h in hs){
2518                         if(hs.hasOwnProperty(h)){
2519                             this.initHeader(h, hs[h], false);
2520                         }
2521                     }
2522                 }
2523                 if(options.xmlData){
2524                     this.initHeader('Content-Type', 'text/xml', false);
2525                     method = 'POST';
2526                     data = options.xmlData;
2527                 }
2528             }
2529
2530             return this.asyncRequest(method, uri, cb, data);
2531         },
2532
2533         serializeForm : function(form) {
2534             if(typeof form == 'string') {
2535                 form = (document.getElementById(form) || document.forms[form]);
2536             }
2537
2538             var el, name, val, disabled, data = '', hasSubmit = false;
2539             for (var i = 0; i < form.elements.length; i++) {
2540                 el = form.elements[i];
2541                 disabled = form.elements[i].disabled;
2542                 name = form.elements[i].name;
2543                 val = form.elements[i].value;
2544
2545                 if (!disabled && name){
2546                     switch (el.type)
2547                             {
2548                         case 'select-one':
2549                         case 'select-multiple':
2550                             for (var j = 0; j < el.options.length; j++) {
2551                                 if (el.options[j].selected) {
2552                                     if (Roo.isIE) {
2553                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2554                                     }
2555                                     else {
2556                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2557                                     }
2558                                 }
2559                             }
2560                             break;
2561                         case 'radio':
2562                         case 'checkbox':
2563                             if (el.checked) {
2564                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2565                             }
2566                             break;
2567                         case 'file':
2568
2569                         case undefined:
2570
2571                         case 'reset':
2572
2573                         case 'button':
2574
2575                             break;
2576                         case 'submit':
2577                             if(hasSubmit == false) {
2578                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2579                                 hasSubmit = true;
2580                             }
2581                             break;
2582                         default:
2583                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             break;
2585                     }
2586                 }
2587             }
2588             data = data.substr(0, data.length - 1);
2589             return data;
2590         },
2591
2592         headers:{},
2593
2594         hasHeaders:false,
2595
2596         useDefaultHeader:true,
2597
2598         defaultPostHeader:'application/x-www-form-urlencoded',
2599
2600         useDefaultXhrHeader:true,
2601
2602         defaultXhrHeader:'XMLHttpRequest',
2603
2604         hasDefaultHeaders:true,
2605
2606         defaultHeaders:{},
2607
2608         poll:{},
2609
2610         timeout:{},
2611
2612         pollInterval:50,
2613
2614         transactionId:0,
2615
2616         setProgId:function(id)
2617         {
2618             this.activeX.unshift(id);
2619         },
2620
2621         setDefaultPostHeader:function(b)
2622         {
2623             this.useDefaultHeader = b;
2624         },
2625
2626         setDefaultXhrHeader:function(b)
2627         {
2628             this.useDefaultXhrHeader = b;
2629         },
2630
2631         setPollingInterval:function(i)
2632         {
2633             if (typeof i == 'number' && isFinite(i)) {
2634                 this.pollInterval = i;
2635             }
2636         },
2637
2638         createXhrObject:function(transactionId)
2639         {
2640             var obj,http;
2641             try
2642             {
2643
2644                 http = new XMLHttpRequest();
2645
2646                 obj = { conn:http, tId:transactionId };
2647             }
2648             catch(e)
2649             {
2650                 for (var i = 0; i < this.activeX.length; ++i) {
2651                     try
2652                     {
2653
2654                         http = new ActiveXObject(this.activeX[i]);
2655
2656                         obj = { conn:http, tId:transactionId };
2657                         break;
2658                     }
2659                     catch(e) {
2660                     }
2661                 }
2662             }
2663             finally
2664             {
2665                 return obj;
2666             }
2667         },
2668
2669         getConnectionObject:function()
2670         {
2671             var o;
2672             var tId = this.transactionId;
2673
2674             try
2675             {
2676                 o = this.createXhrObject(tId);
2677                 if (o) {
2678                     this.transactionId++;
2679                 }
2680             }
2681             catch(e) {
2682             }
2683             finally
2684             {
2685                 return o;
2686             }
2687         },
2688
2689         asyncRequest:function(method, uri, callback, postData)
2690         {
2691             var o = this.getConnectionObject();
2692
2693             if (!o) {
2694                 return null;
2695             }
2696             else {
2697                 o.conn.open(method, uri, true);
2698
2699                 if (this.useDefaultXhrHeader) {
2700                     if (!this.defaultHeaders['X-Requested-With']) {
2701                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2702                     }
2703                 }
2704
2705                 if(postData && this.useDefaultHeader){
2706                     this.initHeader('Content-Type', this.defaultPostHeader);
2707                 }
2708
2709                  if (this.hasDefaultHeaders || this.hasHeaders) {
2710                     this.setHeader(o);
2711                 }
2712
2713                 this.handleReadyState(o, callback);
2714                 o.conn.send(postData || null);
2715
2716                 return o;
2717             }
2718         },
2719
2720         handleReadyState:function(o, callback)
2721         {
2722             var oConn = this;
2723
2724             if (callback && callback.timeout) {
2725                 
2726                 this.timeout[o.tId] = window.setTimeout(function() {
2727                     oConn.abort(o, callback, true);
2728                 }, callback.timeout);
2729             }
2730
2731             this.poll[o.tId] = window.setInterval(
2732                     function() {
2733                         if (o.conn && o.conn.readyState == 4) {
2734                             window.clearInterval(oConn.poll[o.tId]);
2735                             delete oConn.poll[o.tId];
2736
2737                             if(callback && callback.timeout) {
2738                                 window.clearTimeout(oConn.timeout[o.tId]);
2739                                 delete oConn.timeout[o.tId];
2740                             }
2741
2742                             oConn.handleTransactionResponse(o, callback);
2743                         }
2744                     }
2745                     , this.pollInterval);
2746         },
2747
2748         handleTransactionResponse:function(o, callback, isAbort)
2749         {
2750
2751             if (!callback) {
2752                 this.releaseObject(o);
2753                 return;
2754             }
2755
2756             var httpStatus, responseObject;
2757
2758             try
2759             {
2760                 if (o.conn.status !== undefined && o.conn.status != 0) {
2761                     httpStatus = o.conn.status;
2762                 }
2763                 else {
2764                     httpStatus = 13030;
2765                 }
2766             }
2767             catch(e) {
2768
2769
2770                 httpStatus = 13030;
2771             }
2772
2773             if (httpStatus >= 200 && httpStatus < 300) {
2774                 responseObject = this.createResponseObject(o, callback.argument);
2775                 if (callback.success) {
2776                     if (!callback.scope) {
2777                         callback.success(responseObject);
2778                     }
2779                     else {
2780
2781
2782                         callback.success.apply(callback.scope, [responseObject]);
2783                     }
2784                 }
2785             }
2786             else {
2787                 switch (httpStatus) {
2788
2789                     case 12002:
2790                     case 12029:
2791                     case 12030:
2792                     case 12031:
2793                     case 12152:
2794                     case 13030:
2795                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2796                         if (callback.failure) {
2797                             if (!callback.scope) {
2798                                 callback.failure(responseObject);
2799                             }
2800                             else {
2801                                 callback.failure.apply(callback.scope, [responseObject]);
2802                             }
2803                         }
2804                         break;
2805                     default:
2806                         responseObject = this.createResponseObject(o, callback.argument);
2807                         if (callback.failure) {
2808                             if (!callback.scope) {
2809                                 callback.failure(responseObject);
2810                             }
2811                             else {
2812                                 callback.failure.apply(callback.scope, [responseObject]);
2813                             }
2814                         }
2815                 }
2816             }
2817
2818             this.releaseObject(o);
2819             responseObject = null;
2820         },
2821
2822         createResponseObject:function(o, callbackArg)
2823         {
2824             var obj = {};
2825             var headerObj = {};
2826
2827             try
2828             {
2829                 var headerStr = o.conn.getAllResponseHeaders();
2830                 var header = headerStr.split('\n');
2831                 for (var i = 0; i < header.length; i++) {
2832                     var delimitPos = header[i].indexOf(':');
2833                     if (delimitPos != -1) {
2834                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2835                     }
2836                 }
2837             }
2838             catch(e) {
2839             }
2840
2841             obj.tId = o.tId;
2842             obj.status = o.conn.status;
2843             obj.statusText = o.conn.statusText;
2844             obj.getResponseHeader = headerObj;
2845             obj.getAllResponseHeaders = headerStr;
2846             obj.responseText = o.conn.responseText;
2847             obj.responseXML = o.conn.responseXML;
2848
2849             if (typeof callbackArg !== undefined) {
2850                 obj.argument = callbackArg;
2851             }
2852
2853             return obj;
2854         },
2855
2856         createExceptionObject:function(tId, callbackArg, isAbort)
2857         {
2858             var COMM_CODE = 0;
2859             var COMM_ERROR = 'communication failure';
2860             var ABORT_CODE = -1;
2861             var ABORT_ERROR = 'transaction aborted';
2862
2863             var obj = {};
2864
2865             obj.tId = tId;
2866             if (isAbort) {
2867                 obj.status = ABORT_CODE;
2868                 obj.statusText = ABORT_ERROR;
2869             }
2870             else {
2871                 obj.status = COMM_CODE;
2872                 obj.statusText = COMM_ERROR;
2873             }
2874
2875             if (callbackArg) {
2876                 obj.argument = callbackArg;
2877             }
2878
2879             return obj;
2880         },
2881
2882         initHeader:function(label, value, isDefault)
2883         {
2884             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2885
2886             if (headerObj[label] === undefined) {
2887                 headerObj[label] = value;
2888             }
2889             else {
2890
2891
2892                 headerObj[label] = value + "," + headerObj[label];
2893             }
2894
2895             if (isDefault) {
2896                 this.hasDefaultHeaders = true;
2897             }
2898             else {
2899                 this.hasHeaders = true;
2900             }
2901         },
2902
2903
2904         setHeader:function(o)
2905         {
2906             if (this.hasDefaultHeaders) {
2907                 for (var prop in this.defaultHeaders) {
2908                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2909                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2910                     }
2911                 }
2912             }
2913
2914             if (this.hasHeaders) {
2915                 for (var prop in this.headers) {
2916                     if (this.headers.hasOwnProperty(prop)) {
2917                         o.conn.setRequestHeader(prop, this.headers[prop]);
2918                     }
2919                 }
2920                 this.headers = {};
2921                 this.hasHeaders = false;
2922             }
2923         },
2924
2925         resetDefaultHeaders:function() {
2926             delete this.defaultHeaders;
2927             this.defaultHeaders = {};
2928             this.hasDefaultHeaders = false;
2929         },
2930
2931         abort:function(o, callback, isTimeout)
2932         {
2933             if(this.isCallInProgress(o)) {
2934                 o.conn.abort();
2935                 window.clearInterval(this.poll[o.tId]);
2936                 delete this.poll[o.tId];
2937                 if (isTimeout) {
2938                     delete this.timeout[o.tId];
2939                 }
2940
2941                 this.handleTransactionResponse(o, callback, true);
2942
2943                 return true;
2944             }
2945             else {
2946                 return false;
2947             }
2948         },
2949
2950
2951         isCallInProgress:function(o)
2952         {
2953             if (o && o.conn) {
2954                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2955             }
2956             else {
2957
2958                 return false;
2959             }
2960         },
2961
2962
2963         releaseObject:function(o)
2964         {
2965
2966             o.conn = null;
2967
2968             o = null;
2969         },
2970
2971         activeX:[
2972         'MSXML2.XMLHTTP.3.0',
2973         'MSXML2.XMLHTTP',
2974         'Microsoft.XMLHTTP'
2975         ]
2976
2977
2978     };
2979 })();/*
2980  * Portions of this file are based on pieces of Yahoo User Interface Library
2981  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2982  * YUI licensed under the BSD License:
2983  * http://developer.yahoo.net/yui/license.txt
2984  * <script type="text/javascript">
2985  *
2986  */
2987
2988 Roo.lib.Region = function(t, r, b, l) {
2989     this.top = t;
2990     this[1] = t;
2991     this.right = r;
2992     this.bottom = b;
2993     this.left = l;
2994     this[0] = l;
2995 };
2996
2997
2998 Roo.lib.Region.prototype = {
2999     contains : function(region) {
3000         return ( region.left >= this.left &&
3001                  region.right <= this.right &&
3002                  region.top >= this.top &&
3003                  region.bottom <= this.bottom    );
3004
3005     },
3006
3007     getArea : function() {
3008         return ( (this.bottom - this.top) * (this.right - this.left) );
3009     },
3010
3011     intersect : function(region) {
3012         var t = Math.max(this.top, region.top);
3013         var r = Math.min(this.right, region.right);
3014         var b = Math.min(this.bottom, region.bottom);
3015         var l = Math.max(this.left, region.left);
3016
3017         if (b >= t && r >= l) {
3018             return new Roo.lib.Region(t, r, b, l);
3019         } else {
3020             return null;
3021         }
3022     },
3023     union : function(region) {
3024         var t = Math.min(this.top, region.top);
3025         var r = Math.max(this.right, region.right);
3026         var b = Math.max(this.bottom, region.bottom);
3027         var l = Math.min(this.left, region.left);
3028
3029         return new Roo.lib.Region(t, r, b, l);
3030     },
3031
3032     adjust : function(t, l, b, r) {
3033         this.top += t;
3034         this.left += l;
3035         this.right += r;
3036         this.bottom += b;
3037         return this;
3038     }
3039 };
3040
3041 Roo.lib.Region.getRegion = function(el) {
3042     var p = Roo.lib.Dom.getXY(el);
3043
3044     var t = p[1];
3045     var r = p[0] + el.offsetWidth;
3046     var b = p[1] + el.offsetHeight;
3047     var l = p[0];
3048
3049     return new Roo.lib.Region(t, r, b, l);
3050 };
3051 /*
3052  * Portions of this file are based on pieces of Yahoo User Interface Library
3053  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3054  * YUI licensed under the BSD License:
3055  * http://developer.yahoo.net/yui/license.txt
3056  * <script type="text/javascript">
3057  *
3058  */
3059 //@@dep Roo.lib.Region
3060
3061
3062 Roo.lib.Point = function(x, y) {
3063     if (x instanceof Array) {
3064         y = x[1];
3065         x = x[0];
3066     }
3067     this.x = this.right = this.left = this[0] = x;
3068     this.y = this.top = this.bottom = this[1] = y;
3069 };
3070
3071 Roo.lib.Point.prototype = new Roo.lib.Region();
3072 /*
3073  * Portions of this file are based on pieces of Yahoo User Interface Library
3074  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3075  * YUI licensed under the BSD License:
3076  * http://developer.yahoo.net/yui/license.txt
3077  * <script type="text/javascript">
3078  *
3079  */
3080  
3081 (function() {   
3082
3083     Roo.lib.Anim = {
3084         scroll : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3086         },
3087
3088         motion : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3090         },
3091
3092         color : function(el, args, duration, easing, cb, scope) {
3093             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3094         },
3095
3096         run : function(el, args, duration, easing, cb, scope, type) {
3097             type = type || Roo.lib.AnimBase;
3098             if (typeof easing == "string") {
3099                 easing = Roo.lib.Easing[easing];
3100             }
3101             var anim = new type(el, args, duration, easing);
3102             anim.animateX(function() {
3103                 Roo.callback(cb, scope);
3104             });
3105             return anim;
3106         }
3107     };
3108 })();/*
3109  * Portions of this file are based on pieces of Yahoo User Interface Library
3110  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3111  * YUI licensed under the BSD License:
3112  * http://developer.yahoo.net/yui/license.txt
3113  * <script type="text/javascript">
3114  *
3115  */
3116
3117 (function() {    
3118     var libFlyweight;
3119     
3120     function fly(el) {
3121         if (!libFlyweight) {
3122             libFlyweight = new Roo.Element.Flyweight();
3123         }
3124         libFlyweight.dom = el;
3125         return libFlyweight;
3126     }
3127
3128     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3129     
3130    
3131     
3132     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3133         if (el) {
3134             this.init(el, attributes, duration, method);
3135         }
3136     };
3137
3138     Roo.lib.AnimBase.fly = fly;
3139     
3140     
3141     
3142     Roo.lib.AnimBase.prototype = {
3143
3144         toString: function() {
3145             var el = this.getEl();
3146             var id = el.id || el.tagName;
3147             return ("Anim " + id);
3148         },
3149
3150         patterns: {
3151             noNegatives:        /width|height|opacity|padding/i,
3152             offsetAttribute:  /^((width|height)|(top|left))$/,
3153             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3154             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3155         },
3156
3157
3158         doMethod: function(attr, start, end) {
3159             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3160         },
3161
3162
3163         setAttribute: function(attr, val, unit) {
3164             if (this.patterns.noNegatives.test(attr)) {
3165                 val = (val > 0) ? val : 0;
3166             }
3167
3168             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3169         },
3170
3171
3172         getAttribute: function(attr) {
3173             var el = this.getEl();
3174             var val = fly(el).getStyle(attr);
3175
3176             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3177                 return parseFloat(val);
3178             }
3179
3180             var a = this.patterns.offsetAttribute.exec(attr) || [];
3181             var pos = !!( a[3] );
3182             var box = !!( a[2] );
3183
3184
3185             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3186                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3187             } else {
3188                 val = 0;
3189             }
3190
3191             return val;
3192         },
3193
3194
3195         getDefaultUnit: function(attr) {
3196             if (this.patterns.defaultUnit.test(attr)) {
3197                 return 'px';
3198             }
3199
3200             return '';
3201         },
3202
3203         animateX : function(callback, scope) {
3204             var f = function() {
3205                 this.onComplete.removeListener(f);
3206                 if (typeof callback == "function") {
3207                     callback.call(scope || this, this);
3208                 }
3209             };
3210             this.onComplete.addListener(f, this);
3211             this.animate();
3212         },
3213
3214
3215         setRuntimeAttribute: function(attr) {
3216             var start;
3217             var end;
3218             var attributes = this.attributes;
3219
3220             this.runtimeAttributes[attr] = {};
3221
3222             var isset = function(prop) {
3223                 return (typeof prop !== 'undefined');
3224             };
3225
3226             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3227                 return false;
3228             }
3229
3230             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3231
3232
3233             if (isset(attributes[attr]['to'])) {
3234                 end = attributes[attr]['to'];
3235             } else if (isset(attributes[attr]['by'])) {
3236                 if (start.constructor == Array) {
3237                     end = [];
3238                     for (var i = 0, len = start.length; i < len; ++i) {
3239                         end[i] = start[i] + attributes[attr]['by'][i];
3240                     }
3241                 } else {
3242                     end = start + attributes[attr]['by'];
3243                 }
3244             }
3245
3246             this.runtimeAttributes[attr].start = start;
3247             this.runtimeAttributes[attr].end = end;
3248
3249
3250             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3251         },
3252
3253
3254         init: function(el, attributes, duration, method) {
3255
3256             var isAnimated = false;
3257
3258
3259             var startTime = null;
3260
3261
3262             var actualFrames = 0;
3263
3264
3265             el = Roo.getDom(el);
3266
3267
3268             this.attributes = attributes || {};
3269
3270
3271             this.duration = duration || 1;
3272
3273
3274             this.method = method || Roo.lib.Easing.easeNone;
3275
3276
3277             this.useSeconds = true;
3278
3279
3280             this.currentFrame = 0;
3281
3282
3283             this.totalFrames = Roo.lib.AnimMgr.fps;
3284
3285
3286             this.getEl = function() {
3287                 return el;
3288             };
3289
3290
3291             this.isAnimated = function() {
3292                 return isAnimated;
3293             };
3294
3295
3296             this.getStartTime = function() {
3297                 return startTime;
3298             };
3299
3300             this.runtimeAttributes = {};
3301
3302
3303             this.animate = function() {
3304                 if (this.isAnimated()) {
3305                     return false;
3306                 }
3307
3308                 this.currentFrame = 0;
3309
3310                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3311
3312                 Roo.lib.AnimMgr.registerElement(this);
3313             };
3314
3315
3316             this.stop = function(finish) {
3317                 if (finish) {
3318                     this.currentFrame = this.totalFrames;
3319                     this._onTween.fire();
3320                 }
3321                 Roo.lib.AnimMgr.stop(this);
3322             };
3323
3324             var onStart = function() {
3325                 this.onStart.fire();
3326
3327                 this.runtimeAttributes = {};
3328                 for (var attr in this.attributes) {
3329                     this.setRuntimeAttribute(attr);
3330                 }
3331
3332                 isAnimated = true;
3333                 actualFrames = 0;
3334                 startTime = new Date();
3335             };
3336
3337
3338             var onTween = function() {
3339                 var data = {
3340                     duration: new Date() - this.getStartTime(),
3341                     currentFrame: this.currentFrame
3342                 };
3343
3344                 data.toString = function() {
3345                     return (
3346                             'duration: ' + data.duration +
3347                             ', currentFrame: ' + data.currentFrame
3348                             );
3349                 };
3350
3351                 this.onTween.fire(data);
3352
3353                 var runtimeAttributes = this.runtimeAttributes;
3354
3355                 for (var attr in runtimeAttributes) {
3356                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3357                 }
3358
3359                 actualFrames += 1;
3360             };
3361
3362             var onComplete = function() {
3363                 var actual_duration = (new Date() - startTime) / 1000 ;
3364
3365                 var data = {
3366                     duration: actual_duration,
3367                     frames: actualFrames,
3368                     fps: actualFrames / actual_duration
3369                 };
3370
3371                 data.toString = function() {
3372                     return (
3373                             'duration: ' + data.duration +
3374                             ', frames: ' + data.frames +
3375                             ', fps: ' + data.fps
3376                             );
3377                 };
3378
3379                 isAnimated = false;
3380                 actualFrames = 0;
3381                 this.onComplete.fire(data);
3382             };
3383
3384
3385             this._onStart = new Roo.util.Event(this);
3386             this.onStart = new Roo.util.Event(this);
3387             this.onTween = new Roo.util.Event(this);
3388             this._onTween = new Roo.util.Event(this);
3389             this.onComplete = new Roo.util.Event(this);
3390             this._onComplete = new Roo.util.Event(this);
3391             this._onStart.addListener(onStart);
3392             this._onTween.addListener(onTween);
3393             this._onComplete.addListener(onComplete);
3394         }
3395     };
3396 })();
3397 /*
3398  * Portions of this file are based on pieces of Yahoo User Interface Library
3399  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3400  * YUI licensed under the BSD License:
3401  * http://developer.yahoo.net/yui/license.txt
3402  * <script type="text/javascript">
3403  *
3404  */
3405
3406 Roo.lib.AnimMgr = new function() {
3407
3408     var thread = null;
3409
3410
3411     var queue = [];
3412
3413
3414     var tweenCount = 0;
3415
3416
3417     this.fps = 1000;
3418
3419
3420     this.delay = 1;
3421
3422
3423     this.registerElement = function(tween) {
3424         queue[queue.length] = tween;
3425         tweenCount += 1;
3426         tween._onStart.fire();
3427         this.start();
3428     };
3429
3430
3431     this.unRegister = function(tween, index) {
3432         tween._onComplete.fire();
3433         index = index || getIndex(tween);
3434         if (index != -1) {
3435             queue.splice(index, 1);
3436         }
3437
3438         tweenCount -= 1;
3439         if (tweenCount <= 0) {
3440             this.stop();
3441         }
3442     };
3443
3444
3445     this.start = function() {
3446         if (thread === null) {
3447             thread = setInterval(this.run, this.delay);
3448         }
3449     };
3450
3451
3452     this.stop = function(tween) {
3453         if (!tween) {
3454             clearInterval(thread);
3455
3456             for (var i = 0, len = queue.length; i < len; ++i) {
3457                 if (queue[0].isAnimated()) {
3458                     this.unRegister(queue[0], 0);
3459                 }
3460             }
3461
3462             queue = [];
3463             thread = null;
3464             tweenCount = 0;
3465         }
3466         else {
3467             this.unRegister(tween);
3468         }
3469     };
3470
3471
3472     this.run = function() {
3473         for (var i = 0, len = queue.length; i < len; ++i) {
3474             var tween = queue[i];
3475             if (!tween || !tween.isAnimated()) {
3476                 continue;
3477             }
3478
3479             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3480             {
3481                 tween.currentFrame += 1;
3482
3483                 if (tween.useSeconds) {
3484                     correctFrame(tween);
3485                 }
3486                 tween._onTween.fire();
3487             }
3488             else {
3489                 Roo.lib.AnimMgr.stop(tween, i);
3490             }
3491         }
3492     };
3493
3494     var getIndex = function(anim) {
3495         for (var i = 0, len = queue.length; i < len; ++i) {
3496             if (queue[i] == anim) {
3497                 return i;
3498             }
3499         }
3500         return -1;
3501     };
3502
3503
3504     var correctFrame = function(tween) {
3505         var frames = tween.totalFrames;
3506         var frame = tween.currentFrame;
3507         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3508         var elapsed = (new Date() - tween.getStartTime());
3509         var tweak = 0;
3510
3511         if (elapsed < tween.duration * 1000) {
3512             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3513         } else {
3514             tweak = frames - (frame + 1);
3515         }
3516         if (tweak > 0 && isFinite(tweak)) {
3517             if (tween.currentFrame + tweak >= frames) {
3518                 tweak = frames - (frame + 1);
3519             }
3520
3521             tween.currentFrame += tweak;
3522         }
3523     };
3524 };
3525
3526     /*
3527  * Portions of this file are based on pieces of Yahoo User Interface Library
3528  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3529  * YUI licensed under the BSD License:
3530  * http://developer.yahoo.net/yui/license.txt
3531  * <script type="text/javascript">
3532  *
3533  */
3534 Roo.lib.Bezier = new function() {
3535
3536         this.getPosition = function(points, t) {
3537             var n = points.length;
3538             var tmp = [];
3539
3540             for (var i = 0; i < n; ++i) {
3541                 tmp[i] = [points[i][0], points[i][1]];
3542             }
3543
3544             for (var j = 1; j < n; ++j) {
3545                 for (i = 0; i < n - j; ++i) {
3546                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3547                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3548                 }
3549             }
3550
3551             return [ tmp[0][0], tmp[0][1] ];
3552
3553         };
3554     };/*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 (function() {
3563
3564     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3565         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3566     };
3567
3568     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3569
3570     var fly = Roo.lib.AnimBase.fly;
3571     var Y = Roo.lib;
3572     var superclass = Y.ColorAnim.superclass;
3573     var proto = Y.ColorAnim.prototype;
3574
3575     proto.toString = function() {
3576         var el = this.getEl();
3577         var id = el.id || el.tagName;
3578         return ("ColorAnim " + id);
3579     };
3580
3581     proto.patterns.color = /color$/i;
3582     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3583     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3584     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3585     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3586
3587
3588     proto.parseColor = function(s) {
3589         if (s.length == 3) {
3590             return s;
3591         }
3592
3593         var c = this.patterns.hex.exec(s);
3594         if (c && c.length == 4) {
3595             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3596         }
3597
3598         c = this.patterns.rgb.exec(s);
3599         if (c && c.length == 4) {
3600             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3601         }
3602
3603         c = this.patterns.hex3.exec(s);
3604         if (c && c.length == 4) {
3605             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3606         }
3607
3608         return null;
3609     };
3610     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3611     proto.getAttribute = function(attr) {
3612         var el = this.getEl();
3613         if (this.patterns.color.test(attr)) {
3614             var val = fly(el).getStyle(attr);
3615
3616             if (this.patterns.transparent.test(val)) {
3617                 var parent = el.parentNode;
3618                 val = fly(parent).getStyle(attr);
3619
3620                 while (parent && this.patterns.transparent.test(val)) {
3621                     parent = parent.parentNode;
3622                     val = fly(parent).getStyle(attr);
3623                     if (parent.tagName.toUpperCase() == 'HTML') {
3624                         val = '#fff';
3625                     }
3626                 }
3627             }
3628         } else {
3629             val = superclass.getAttribute.call(this, attr);
3630         }
3631
3632         return val;
3633     };
3634     proto.getAttribute = function(attr) {
3635         var el = this.getEl();
3636         if (this.patterns.color.test(attr)) {
3637             var val = fly(el).getStyle(attr);
3638
3639             if (this.patterns.transparent.test(val)) {
3640                 var parent = el.parentNode;
3641                 val = fly(parent).getStyle(attr);
3642
3643                 while (parent && this.patterns.transparent.test(val)) {
3644                     parent = parent.parentNode;
3645                     val = fly(parent).getStyle(attr);
3646                     if (parent.tagName.toUpperCase() == 'HTML') {
3647                         val = '#fff';
3648                     }
3649                 }
3650             }
3651         } else {
3652             val = superclass.getAttribute.call(this, attr);
3653         }
3654
3655         return val;
3656     };
3657
3658     proto.doMethod = function(attr, start, end) {
3659         var val;
3660
3661         if (this.patterns.color.test(attr)) {
3662             val = [];
3663             for (var i = 0, len = start.length; i < len; ++i) {
3664                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3665             }
3666
3667             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3668         }
3669         else {
3670             val = superclass.doMethod.call(this, attr, start, end);
3671         }
3672
3673         return val;
3674     };
3675
3676     proto.setRuntimeAttribute = function(attr) {
3677         superclass.setRuntimeAttribute.call(this, attr);
3678
3679         if (this.patterns.color.test(attr)) {
3680             var attributes = this.attributes;
3681             var start = this.parseColor(this.runtimeAttributes[attr].start);
3682             var end = this.parseColor(this.runtimeAttributes[attr].end);
3683
3684             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3685                 end = this.parseColor(attributes[attr].by);
3686
3687                 for (var i = 0, len = start.length; i < len; ++i) {
3688                     end[i] = start[i] + end[i];
3689                 }
3690             }
3691
3692             this.runtimeAttributes[attr].start = start;
3693             this.runtimeAttributes[attr].end = end;
3694         }
3695     };
3696 })();
3697
3698 /*
3699  * Portions of this file are based on pieces of Yahoo User Interface Library
3700  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3701  * YUI licensed under the BSD License:
3702  * http://developer.yahoo.net/yui/license.txt
3703  * <script type="text/javascript">
3704  *
3705  */
3706 Roo.lib.Easing = {
3707
3708
3709     easeNone: function (t, b, c, d) {
3710         return c * t / d + b;
3711     },
3712
3713
3714     easeIn: function (t, b, c, d) {
3715         return c * (t /= d) * t + b;
3716     },
3717
3718
3719     easeOut: function (t, b, c, d) {
3720         return -c * (t /= d) * (t - 2) + b;
3721     },
3722
3723
3724     easeBoth: function (t, b, c, d) {
3725         if ((t /= d / 2) < 1) {
3726             return c / 2 * t * t + b;
3727         }
3728
3729         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3730     },
3731
3732
3733     easeInStrong: function (t, b, c, d) {
3734         return c * (t /= d) * t * t * t + b;
3735     },
3736
3737
3738     easeOutStrong: function (t, b, c, d) {
3739         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3740     },
3741
3742
3743     easeBothStrong: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t * t * t + b;
3746         }
3747
3748         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3749     },
3750
3751
3752
3753     elasticIn: function (t, b, c, d, a, p) {
3754         if (t == 0) {
3755             return b;
3756         }
3757         if ((t /= d) == 1) {
3758             return b + c;
3759         }
3760         if (!p) {
3761             p = d * .3;
3762         }
3763
3764         if (!a || a < Math.abs(c)) {
3765             a = c;
3766             var s = p / 4;
3767         }
3768         else {
3769             var s = p / (2 * Math.PI) * Math.asin(c / a);
3770         }
3771
3772         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3773     },
3774
3775
3776     elasticOut: function (t, b, c, d, a, p) {
3777         if (t == 0) {
3778             return b;
3779         }
3780         if ((t /= d) == 1) {
3781             return b + c;
3782         }
3783         if (!p) {
3784             p = d * .3;
3785         }
3786
3787         if (!a || a < Math.abs(c)) {
3788             a = c;
3789             var s = p / 4;
3790         }
3791         else {
3792             var s = p / (2 * Math.PI) * Math.asin(c / a);
3793         }
3794
3795         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3796     },
3797
3798
3799     elasticBoth: function (t, b, c, d, a, p) {
3800         if (t == 0) {
3801             return b;
3802         }
3803
3804         if ((t /= d / 2) == 2) {
3805             return b + c;
3806         }
3807
3808         if (!p) {
3809             p = d * (.3 * 1.5);
3810         }
3811
3812         if (!a || a < Math.abs(c)) {
3813             a = c;
3814             var s = p / 4;
3815         }
3816         else {
3817             var s = p / (2 * Math.PI) * Math.asin(c / a);
3818         }
3819
3820         if (t < 1) {
3821             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3822                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3823         }
3824         return a * Math.pow(2, -10 * (t -= 1)) *
3825                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3826     },
3827
3828
3829
3830     backIn: function (t, b, c, d, s) {
3831         if (typeof s == 'undefined') {
3832             s = 1.70158;
3833         }
3834         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3835     },
3836
3837
3838     backOut: function (t, b, c, d, s) {
3839         if (typeof s == 'undefined') {
3840             s = 1.70158;
3841         }
3842         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3843     },
3844
3845
3846     backBoth: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850
3851         if ((t /= d / 2 ) < 1) {
3852             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3853         }
3854         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3855     },
3856
3857
3858     bounceIn: function (t, b, c, d) {
3859         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3860     },
3861
3862
3863     bounceOut: function (t, b, c, d) {
3864         if ((t /= d) < (1 / 2.75)) {
3865             return c * (7.5625 * t * t) + b;
3866         } else if (t < (2 / 2.75)) {
3867             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3868         } else if (t < (2.5 / 2.75)) {
3869             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3870         }
3871         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3872     },
3873
3874
3875     bounceBoth: function (t, b, c, d) {
3876         if (t < d / 2) {
3877             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3878         }
3879         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3880     }
3881 };/*
3882  * Portions of this file are based on pieces of Yahoo User Interface Library
3883  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3884  * YUI licensed under the BSD License:
3885  * http://developer.yahoo.net/yui/license.txt
3886  * <script type="text/javascript">
3887  *
3888  */
3889     (function() {
3890         Roo.lib.Motion = function(el, attributes, duration, method) {
3891             if (el) {
3892                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3893             }
3894         };
3895
3896         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3897
3898
3899         var Y = Roo.lib;
3900         var superclass = Y.Motion.superclass;
3901         var proto = Y.Motion.prototype;
3902
3903         proto.toString = function() {
3904             var el = this.getEl();
3905             var id = el.id || el.tagName;
3906             return ("Motion " + id);
3907         };
3908
3909         proto.patterns.points = /^points$/i;
3910
3911         proto.setAttribute = function(attr, val, unit) {
3912             if (this.patterns.points.test(attr)) {
3913                 unit = unit || 'px';
3914                 superclass.setAttribute.call(this, 'left', val[0], unit);
3915                 superclass.setAttribute.call(this, 'top', val[1], unit);
3916             } else {
3917                 superclass.setAttribute.call(this, attr, val, unit);
3918             }
3919         };
3920
3921         proto.getAttribute = function(attr) {
3922             if (this.patterns.points.test(attr)) {
3923                 var val = [
3924                         superclass.getAttribute.call(this, 'left'),
3925                         superclass.getAttribute.call(this, 'top')
3926                         ];
3927             } else {
3928                 val = superclass.getAttribute.call(this, attr);
3929             }
3930
3931             return val;
3932         };
3933
3934         proto.doMethod = function(attr, start, end) {
3935             var val = null;
3936
3937             if (this.patterns.points.test(attr)) {
3938                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3939                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3940             } else {
3941                 val = superclass.doMethod.call(this, attr, start, end);
3942             }
3943             return val;
3944         };
3945
3946         proto.setRuntimeAttribute = function(attr) {
3947             if (this.patterns.points.test(attr)) {
3948                 var el = this.getEl();
3949                 var attributes = this.attributes;
3950                 var start;
3951                 var control = attributes['points']['control'] || [];
3952                 var end;
3953                 var i, len;
3954
3955                 if (control.length > 0 && !(control[0] instanceof Array)) {
3956                     control = [control];
3957                 } else {
3958                     var tmp = [];
3959                     for (i = 0,len = control.length; i < len; ++i) {
3960                         tmp[i] = control[i];
3961                     }
3962                     control = tmp;
3963                 }
3964
3965                 Roo.fly(el).position();
3966
3967                 if (isset(attributes['points']['from'])) {
3968                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3969                 }
3970                 else {
3971                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3972                 }
3973
3974                 start = this.getAttribute('points');
3975
3976
3977                 if (isset(attributes['points']['to'])) {
3978                     end = translateValues.call(this, attributes['points']['to'], start);
3979
3980                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3981                     for (i = 0,len = control.length; i < len; ++i) {
3982                         control[i] = translateValues.call(this, control[i], start);
3983                     }
3984
3985
3986                 } else if (isset(attributes['points']['by'])) {
3987                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3988
3989                     for (i = 0,len = control.length; i < len; ++i) {
3990                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3991                     }
3992                 }
3993
3994                 this.runtimeAttributes[attr] = [start];
3995
3996                 if (control.length > 0) {
3997                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3998                 }
3999
4000                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4001             }
4002             else {
4003                 superclass.setRuntimeAttribute.call(this, attr);
4004             }
4005         };
4006
4007         var translateValues = function(val, start) {
4008             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4010
4011             return val;
4012         };
4013
4014         var isset = function(prop) {
4015             return (typeof prop !== 'undefined');
4016         };
4017     })();
4018 /*
4019  * Portions of this file are based on pieces of Yahoo User Interface Library
4020  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4021  * YUI licensed under the BSD License:
4022  * http://developer.yahoo.net/yui/license.txt
4023  * <script type="text/javascript">
4024  *
4025  */
4026     (function() {
4027         Roo.lib.Scroll = function(el, attributes, duration, method) {
4028             if (el) {
4029                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4030             }
4031         };
4032
4033         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4034
4035
4036         var Y = Roo.lib;
4037         var superclass = Y.Scroll.superclass;
4038         var proto = Y.Scroll.prototype;
4039
4040         proto.toString = function() {
4041             var el = this.getEl();
4042             var id = el.id || el.tagName;
4043             return ("Scroll " + id);
4044         };
4045
4046         proto.doMethod = function(attr, start, end) {
4047             var val = null;
4048
4049             if (attr == 'scroll') {
4050                 val = [
4051                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4052                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4053                         ];
4054
4055             } else {
4056                 val = superclass.doMethod.call(this, attr, start, end);
4057             }
4058             return val;
4059         };
4060
4061         proto.getAttribute = function(attr) {
4062             var val = null;
4063             var el = this.getEl();
4064
4065             if (attr == 'scroll') {
4066                 val = [ el.scrollLeft, el.scrollTop ];
4067             } else {
4068                 val = superclass.getAttribute.call(this, attr);
4069             }
4070
4071             return val;
4072         };
4073
4074         proto.setAttribute = function(attr, val, unit) {
4075             var el = this.getEl();
4076
4077             if (attr == 'scroll') {
4078                 el.scrollLeft = val[0];
4079                 el.scrollTop = val[1];
4080             } else {
4081                 superclass.setAttribute.call(this, attr, val, unit);
4082             }
4083         };
4084     })();
4085 /*
4086  * Based on:
4087  * Ext JS Library 1.1.1
4088  * Copyright(c) 2006-2007, Ext JS, LLC.
4089  *
4090  * Originally Released Under LGPL - original licence link has changed is not relivant.
4091  *
4092  * Fork - LGPL
4093  * <script type="text/javascript">
4094  */
4095
4096
4097 // nasty IE9 hack - what a pile of crap that is..
4098
4099  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4100     Range.prototype.createContextualFragment = function (html) {
4101         var doc = window.document;
4102         var container = doc.createElement("div");
4103         container.innerHTML = html;
4104         var frag = doc.createDocumentFragment(), n;
4105         while ((n = container.firstChild)) {
4106             frag.appendChild(n);
4107         }
4108         return frag;
4109     };
4110 }
4111
4112 /**
4113  * @class Roo.DomHelper
4114  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4115  * 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>.
4116  * @singleton
4117  */
4118 Roo.DomHelper = function(){
4119     var tempTableEl = null;
4120     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4121     var tableRe = /^table|tbody|tr|td$/i;
4122     var xmlns = {};
4123     // build as innerHTML where available
4124     /** @ignore */
4125     var createHtml = function(o){
4126         if(typeof o == 'string'){
4127             return o;
4128         }
4129         var b = "";
4130         if(!o.tag){
4131             o.tag = "div";
4132         }
4133         b += "<" + o.tag;
4134         for(var attr in o){
4135             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4136             if(attr == "style"){
4137                 var s = o["style"];
4138                 if(typeof s == "function"){
4139                     s = s.call();
4140                 }
4141                 if(typeof s == "string"){
4142                     b += ' style="' + s + '"';
4143                 }else if(typeof s == "object"){
4144                     b += ' style="';
4145                     for(var key in s){
4146                         if(typeof s[key] != "function"){
4147                             b += key + ":" + s[key] + ";";
4148                         }
4149                     }
4150                     b += '"';
4151                 }
4152             }else{
4153                 if(attr == "cls"){
4154                     b += ' class="' + o["cls"] + '"';
4155                 }else if(attr == "htmlFor"){
4156                     b += ' for="' + o["htmlFor"] + '"';
4157                 }else{
4158                     b += " " + attr + '="' + o[attr] + '"';
4159                 }
4160             }
4161         }
4162         if(emptyTags.test(o.tag)){
4163             b += "/>";
4164         }else{
4165             b += ">";
4166             var cn = o.children || o.cn;
4167             if(cn){
4168                 //http://bugs.kde.org/show_bug.cgi?id=71506
4169                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4170                     for(var i = 0, len = cn.length; i < len; i++) {
4171                         b += createHtml(cn[i], b);
4172                     }
4173                 }else{
4174                     b += createHtml(cn, b);
4175                 }
4176             }
4177             if(o.html){
4178                 b += o.html;
4179             }
4180             b += "</" + o.tag + ">";
4181         }
4182         return b;
4183     };
4184
4185     // build as dom
4186     /** @ignore */
4187     var createDom = function(o, parentNode){
4188          
4189         // defininition craeted..
4190         var ns = false;
4191         if (o.ns && o.ns != 'html') {
4192                
4193             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4194                 xmlns[o.ns] = o.xmlns;
4195                 ns = o.xmlns;
4196             }
4197             if (typeof(xmlns[o.ns]) == 'undefined') {
4198                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4199             }
4200             ns = xmlns[o.ns];
4201         }
4202         
4203         
4204         if (typeof(o) == 'string') {
4205             return parentNode.appendChild(document.createTextNode(o));
4206         }
4207         o.tag = o.tag || div;
4208         if (o.ns && Roo.isIE) {
4209             ns = false;
4210             o.tag = o.ns + ':' + o.tag;
4211             
4212         }
4213         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4214         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4215         for(var attr in o){
4216             
4217             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4218                     attr == "style" || typeof o[attr] == "function") continue;
4219                     
4220             if(attr=="cls" && Roo.isIE){
4221                 el.className = o["cls"];
4222             }else{
4223                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4224                 else el[attr] = o[attr];
4225             }
4226         }
4227         Roo.DomHelper.applyStyles(el, o.style);
4228         var cn = o.children || o.cn;
4229         if(cn){
4230             //http://bugs.kde.org/show_bug.cgi?id=71506
4231              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4232                 for(var i = 0, len = cn.length; i < len; i++) {
4233                     createDom(cn[i], el);
4234                 }
4235             }else{
4236                 createDom(cn, el);
4237             }
4238         }
4239         if(o.html){
4240             el.innerHTML = o.html;
4241         }
4242         if(parentNode){
4243            parentNode.appendChild(el);
4244         }
4245         return el;
4246     };
4247
4248     var ieTable = function(depth, s, h, e){
4249         tempTableEl.innerHTML = [s, h, e].join('');
4250         var i = -1, el = tempTableEl;
4251         while(++i < depth){
4252             el = el.firstChild;
4253         }
4254         return el;
4255     };
4256
4257     // kill repeat to save bytes
4258     var ts = '<table>',
4259         te = '</table>',
4260         tbs = ts+'<tbody>',
4261         tbe = '</tbody>'+te,
4262         trs = tbs + '<tr>',
4263         tre = '</tr>'+tbe;
4264
4265     /**
4266      * @ignore
4267      * Nasty code for IE's broken table implementation
4268      */
4269     var insertIntoTable = function(tag, where, el, html){
4270         if(!tempTableEl){
4271             tempTableEl = document.createElement('div');
4272         }
4273         var node;
4274         var before = null;
4275         if(tag == 'td'){
4276             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4277                 return;
4278             }
4279             if(where == 'beforebegin'){
4280                 before = el;
4281                 el = el.parentNode;
4282             } else{
4283                 before = el.nextSibling;
4284                 el = el.parentNode;
4285             }
4286             node = ieTable(4, trs, html, tre);
4287         }
4288         else if(tag == 'tr'){
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else if(where == 'afterend'){
4294                 before = el.nextSibling;
4295                 el = el.parentNode;
4296                 node = ieTable(3, tbs, html, tbe);
4297             } else{ // INTO a TR
4298                 if(where == 'afterbegin'){
4299                     before = el.firstChild;
4300                 }
4301                 node = ieTable(4, trs, html, tre);
4302             }
4303         } else if(tag == 'tbody'){
4304             if(where == 'beforebegin'){
4305                 before = el;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else if(where == 'afterend'){
4309                 before = el.nextSibling;
4310                 el = el.parentNode;
4311                 node = ieTable(2, ts, html, te);
4312             } else{
4313                 if(where == 'afterbegin'){
4314                     before = el.firstChild;
4315                 }
4316                 node = ieTable(3, tbs, html, tbe);
4317             }
4318         } else{ // TABLE
4319             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4320                 return;
4321             }
4322             if(where == 'afterbegin'){
4323                 before = el.firstChild;
4324             }
4325             node = ieTable(2, ts, html, te);
4326         }
4327         el.insertBefore(node, before);
4328         return node;
4329     };
4330
4331     return {
4332     /** True to force the use of DOM instead of html fragments @type Boolean */
4333     useDom : false,
4334
4335     /**
4336      * Returns the markup for the passed Element(s) config
4337      * @param {Object} o The Dom object spec (and children)
4338      * @return {String}
4339      */
4340     markup : function(o){
4341         return createHtml(o);
4342     },
4343
4344     /**
4345      * Applies a style specification to an element
4346      * @param {String/HTMLElement} el The element to apply styles to
4347      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4348      * a function which returns such a specification.
4349      */
4350     applyStyles : function(el, styles){
4351         if(styles){
4352            el = Roo.fly(el);
4353            if(typeof styles == "string"){
4354                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4355                var matches;
4356                while ((matches = re.exec(styles)) != null){
4357                    el.setStyle(matches[1], matches[2]);
4358                }
4359            }else if (typeof styles == "object"){
4360                for (var style in styles){
4361                   el.setStyle(style, styles[style]);
4362                }
4363            }else if (typeof styles == "function"){
4364                 Roo.DomHelper.applyStyles(el, styles.call());
4365            }
4366         }
4367     },
4368
4369     /**
4370      * Inserts an HTML fragment into the Dom
4371      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4372      * @param {HTMLElement} el The context element
4373      * @param {String} html The HTML fragmenet
4374      * @return {HTMLElement} The new node
4375      */
4376     insertHtml : function(where, el, html){
4377         where = where.toLowerCase();
4378         if(el.insertAdjacentHTML){
4379             if(tableRe.test(el.tagName)){
4380                 var rs;
4381                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4382                     return rs;
4383                 }
4384             }
4385             switch(where){
4386                 case "beforebegin":
4387                     el.insertAdjacentHTML('BeforeBegin', html);
4388                     return el.previousSibling;
4389                 case "afterbegin":
4390                     el.insertAdjacentHTML('AfterBegin', html);
4391                     return el.firstChild;
4392                 case "beforeend":
4393                     el.insertAdjacentHTML('BeforeEnd', html);
4394                     return el.lastChild;
4395                 case "afterend":
4396                     el.insertAdjacentHTML('AfterEnd', html);
4397                     return el.nextSibling;
4398             }
4399             throw 'Illegal insertion point -> "' + where + '"';
4400         }
4401         var range = el.ownerDocument.createRange();
4402         var frag;
4403         switch(where){
4404              case "beforebegin":
4405                 range.setStartBefore(el);
4406                 frag = range.createContextualFragment(html);
4407                 el.parentNode.insertBefore(frag, el);
4408                 return el.previousSibling;
4409              case "afterbegin":
4410                 if(el.firstChild){
4411                     range.setStartBefore(el.firstChild);
4412                     frag = range.createContextualFragment(html);
4413                     el.insertBefore(frag, el.firstChild);
4414                     return el.firstChild;
4415                 }else{
4416                     el.innerHTML = html;
4417                     return el.firstChild;
4418                 }
4419             case "beforeend":
4420                 if(el.lastChild){
4421                     range.setStartAfter(el.lastChild);
4422                     frag = range.createContextualFragment(html);
4423                     el.appendChild(frag);
4424                     return el.lastChild;
4425                 }else{
4426                     el.innerHTML = html;
4427                     return el.lastChild;
4428                 }
4429             case "afterend":
4430                 range.setStartAfter(el);
4431                 frag = range.createContextualFragment(html);
4432                 el.parentNode.insertBefore(frag, el.nextSibling);
4433                 return el.nextSibling;
4434             }
4435             throw 'Illegal insertion point -> "' + where + '"';
4436     },
4437
4438     /**
4439      * Creates new Dom element(s) and inserts them before el
4440      * @param {String/HTMLElement/Element} el The context element
4441      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4442      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4443      * @return {HTMLElement/Roo.Element} The new node
4444      */
4445     insertBefore : function(el, o, returnElement){
4446         return this.doInsert(el, o, returnElement, "beforeBegin");
4447     },
4448
4449     /**
4450      * Creates new Dom element(s) and inserts them after el
4451      * @param {String/HTMLElement/Element} el The context element
4452      * @param {Object} o The Dom object spec (and children)
4453      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4454      * @return {HTMLElement/Roo.Element} The new node
4455      */
4456     insertAfter : function(el, o, returnElement){
4457         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4458     },
4459
4460     /**
4461      * Creates new Dom element(s) and inserts them as the first child of el
4462      * @param {String/HTMLElement/Element} el The context element
4463      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4464      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4465      * @return {HTMLElement/Roo.Element} The new node
4466      */
4467     insertFirst : function(el, o, returnElement){
4468         return this.doInsert(el, o, returnElement, "afterBegin");
4469     },
4470
4471     // private
4472     doInsert : function(el, o, returnElement, pos, sibling){
4473         el = Roo.getDom(el);
4474         var newNode;
4475         if(this.useDom || o.ns){
4476             newNode = createDom(o, null);
4477             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4478         }else{
4479             var html = createHtml(o);
4480             newNode = this.insertHtml(pos, el, html);
4481         }
4482         return returnElement ? Roo.get(newNode, true) : newNode;
4483     },
4484
4485     /**
4486      * Creates new Dom element(s) and appends them to el
4487      * @param {String/HTMLElement/Element} el The context element
4488      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4489      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4490      * @return {HTMLElement/Roo.Element} The new node
4491      */
4492     append : function(el, o, returnElement){
4493         el = Roo.getDom(el);
4494         var newNode;
4495         if(this.useDom || o.ns){
4496             newNode = createDom(o, null);
4497             el.appendChild(newNode);
4498         }else{
4499             var html = createHtml(o);
4500             newNode = this.insertHtml("beforeEnd", el, html);
4501         }
4502         return returnElement ? Roo.get(newNode, true) : newNode;
4503     },
4504
4505     /**
4506      * Creates new Dom element(s) and overwrites the contents of el with them
4507      * @param {String/HTMLElement/Element} el The context element
4508      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4509      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4510      * @return {HTMLElement/Roo.Element} The new node
4511      */
4512     overwrite : function(el, o, returnElement){
4513         el = Roo.getDom(el);
4514         if (o.ns) {
4515           
4516             while (el.childNodes.length) {
4517                 el.removeChild(el.firstChild);
4518             }
4519             createDom(o, el);
4520         } else {
4521             el.innerHTML = createHtml(o);   
4522         }
4523         
4524         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4525     },
4526
4527     /**
4528      * Creates a new Roo.DomHelper.Template from the Dom object spec
4529      * @param {Object} o The Dom object spec (and children)
4530      * @return {Roo.DomHelper.Template} The new template
4531      */
4532     createTemplate : function(o){
4533         var html = createHtml(o);
4534         return new Roo.Template(html);
4535     }
4536     };
4537 }();
4538 /*
4539  * Based on:
4540  * Ext JS Library 1.1.1
4541  * Copyright(c) 2006-2007, Ext JS, LLC.
4542  *
4543  * Originally Released Under LGPL - original licence link has changed is not relivant.
4544  *
4545  * Fork - LGPL
4546  * <script type="text/javascript">
4547  */
4548  
4549 /**
4550 * @class Roo.Template
4551 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4552 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4553 * Usage:
4554 <pre><code>
4555 var t = new Roo.Template({
4556     html :  '&lt;div name="{id}"&gt;' + 
4557         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4558         '&lt;/div&gt;',
4559     myformat: function (value, allValues) {
4560         return 'XX' + value;
4561     }
4562 });
4563 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4564 </code></pre>
4565 * For more information see this blog post with examples:
4566 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4567      - Create Elements using DOM, HTML fragments and Templates</a>. 
4568 * @constructor
4569 * @param {Object} cfg - Configuration object.
4570 */
4571 Roo.Template = function(cfg){
4572     // BC!
4573     if(cfg instanceof Array){
4574         cfg = cfg.join("");
4575     }else if(arguments.length > 1){
4576         cfg = Array.prototype.join.call(arguments, "");
4577     }
4578     
4579     
4580     if (typeof(cfg) == 'object') {
4581         Roo.apply(this,cfg)
4582     } else {
4583         // bc
4584         this.html = cfg;
4585     }
4586     if (this.url) {
4587         this.load();
4588     }
4589     
4590 };
4591 Roo.Template.prototype = {
4592     
4593     /**
4594      * @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..
4595      *                    it should be fixed so that template is observable...
4596      */
4597     url : false,
4598     /**
4599      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4600      */
4601     html : '',
4602     /**
4603      * Returns an HTML fragment of this template with the specified values applied.
4604      * @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'})
4605      * @return {String} The HTML fragment
4606      */
4607     applyTemplate : function(values){
4608         try {
4609            
4610             if(this.compiled){
4611                 return this.compiled(values);
4612             }
4613             var useF = this.disableFormats !== true;
4614             var fm = Roo.util.Format, tpl = this;
4615             var fn = function(m, name, format, args){
4616                 if(format && useF){
4617                     if(format.substr(0, 5) == "this."){
4618                         return tpl.call(format.substr(5), values[name], values);
4619                     }else{
4620                         if(args){
4621                             // quoted values are required for strings in compiled templates, 
4622                             // but for non compiled we need to strip them
4623                             // quoted reversed for jsmin
4624                             var re = /^\s*['"](.*)["']\s*$/;
4625                             args = args.split(',');
4626                             for(var i = 0, len = args.length; i < len; i++){
4627                                 args[i] = args[i].replace(re, "$1");
4628                             }
4629                             args = [values[name]].concat(args);
4630                         }else{
4631                             args = [values[name]];
4632                         }
4633                         return fm[format].apply(fm, args);
4634                     }
4635                 }else{
4636                     return values[name] !== undefined ? values[name] : "";
4637                 }
4638             };
4639             return this.html.replace(this.re, fn);
4640         } catch (e) {
4641             Roo.log(e);
4642             throw e;
4643         }
4644          
4645     },
4646     
4647     loading : false,
4648       
4649     load : function ()
4650     {
4651          
4652         if (this.loading) {
4653             return;
4654         }
4655         var _t = this;
4656         
4657         this.loading = true;
4658         this.compiled = false;
4659         
4660         var cx = new Roo.data.Connection();
4661         cx.request({
4662             url : this.url,
4663             method : 'GET',
4664             success : function (response) {
4665                 _t.loading = false;
4666                 _t.html = response.responseText;
4667                 _t.url = false;
4668                 _t.compile();
4669              },
4670             failure : function(response) {
4671                 Roo.log("Template failed to load from " + _t.url);
4672                 _t.loading = false;
4673             }
4674         });
4675     },
4676
4677     /**
4678      * Sets the HTML used as the template and optionally compiles it.
4679      * @param {String} html
4680      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4681      * @return {Roo.Template} this
4682      */
4683     set : function(html, compile){
4684         this.html = html;
4685         this.compiled = null;
4686         if(compile){
4687             this.compile();
4688         }
4689         return this;
4690     },
4691     
4692     /**
4693      * True to disable format functions (defaults to false)
4694      * @type Boolean
4695      */
4696     disableFormats : false,
4697     
4698     /**
4699     * The regular expression used to match template variables 
4700     * @type RegExp
4701     * @property 
4702     */
4703     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4704     
4705     /**
4706      * Compiles the template into an internal function, eliminating the RegEx overhead.
4707      * @return {Roo.Template} this
4708      */
4709     compile : function(){
4710         var fm = Roo.util.Format;
4711         var useF = this.disableFormats !== true;
4712         var sep = Roo.isGecko ? "+" : ",";
4713         var fn = function(m, name, format, args){
4714             if(format && useF){
4715                 args = args ? ',' + args : "";
4716                 if(format.substr(0, 5) != "this."){
4717                     format = "fm." + format + '(';
4718                 }else{
4719                     format = 'this.call("'+ format.substr(5) + '", ';
4720                     args = ", values";
4721                 }
4722             }else{
4723                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4724             }
4725             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4726         };
4727         var body;
4728         // branched to use + in gecko and [].join() in others
4729         if(Roo.isGecko){
4730             body = "this.compiled = function(values){ return '" +
4731                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4732                     "';};";
4733         }else{
4734             body = ["this.compiled = function(values){ return ['"];
4735             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4736             body.push("'].join('');};");
4737             body = body.join('');
4738         }
4739         /**
4740          * eval:var:values
4741          * eval:var:fm
4742          */
4743         eval(body);
4744         return this;
4745     },
4746     
4747     // private function used to call members
4748     call : function(fnName, value, allValues){
4749         return this[fnName](value, allValues);
4750     },
4751     
4752     /**
4753      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4754      * @param {String/HTMLElement/Roo.Element} el The context element
4755      * @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'})
4756      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4757      * @return {HTMLElement/Roo.Element} The new node or Element
4758      */
4759     insertFirst: function(el, values, returnElement){
4760         return this.doInsert('afterBegin', el, values, returnElement);
4761     },
4762
4763     /**
4764      * Applies the supplied values to the template and inserts the new node(s) before el.
4765      * @param {String/HTMLElement/Roo.Element} el The context element
4766      * @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'})
4767      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4768      * @return {HTMLElement/Roo.Element} The new node or Element
4769      */
4770     insertBefore: function(el, values, returnElement){
4771         return this.doInsert('beforeBegin', el, values, returnElement);
4772     },
4773
4774     /**
4775      * Applies the supplied values to the template and inserts the new node(s) after el.
4776      * @param {String/HTMLElement/Roo.Element} el The context element
4777      * @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'})
4778      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4779      * @return {HTMLElement/Roo.Element} The new node or Element
4780      */
4781     insertAfter : function(el, values, returnElement){
4782         return this.doInsert('afterEnd', el, values, returnElement);
4783     },
4784     
4785     /**
4786      * Applies the supplied values to the template and appends the new node(s) to el.
4787      * @param {String/HTMLElement/Roo.Element} el The context element
4788      * @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'})
4789      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4790      * @return {HTMLElement/Roo.Element} The new node or Element
4791      */
4792     append : function(el, values, returnElement){
4793         return this.doInsert('beforeEnd', el, values, returnElement);
4794     },
4795
4796     doInsert : function(where, el, values, returnEl){
4797         el = Roo.getDom(el);
4798         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4799         return returnEl ? Roo.get(newNode, true) : newNode;
4800     },
4801
4802     /**
4803      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4804      * @param {String/HTMLElement/Roo.Element} el The context element
4805      * @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'})
4806      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4807      * @return {HTMLElement/Roo.Element} The new node or Element
4808      */
4809     overwrite : function(el, values, returnElement){
4810         el = Roo.getDom(el);
4811         el.innerHTML = this.applyTemplate(values);
4812         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4813     }
4814 };
4815 /**
4816  * Alias for {@link #applyTemplate}
4817  * @method
4818  */
4819 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4820
4821 // backwards compat
4822 Roo.DomHelper.Template = Roo.Template;
4823
4824 /**
4825  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4826  * @param {String/HTMLElement} el A DOM element or its id
4827  * @returns {Roo.Template} The created template
4828  * @static
4829  */
4830 Roo.Template.from = function(el){
4831     el = Roo.getDom(el);
4832     return new Roo.Template(el.value || el.innerHTML);
4833 };/*
4834  * Based on:
4835  * Ext JS Library 1.1.1
4836  * Copyright(c) 2006-2007, Ext JS, LLC.
4837  *
4838  * Originally Released Under LGPL - original licence link has changed is not relivant.
4839  *
4840  * Fork - LGPL
4841  * <script type="text/javascript">
4842  */
4843  
4844
4845 /*
4846  * This is code is also distributed under MIT license for use
4847  * with jQuery and prototype JavaScript libraries.
4848  */
4849 /**
4850  * @class Roo.DomQuery
4851 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).
4852 <p>
4853 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>
4854
4855 <p>
4856 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.
4857 </p>
4858 <h4>Element Selectors:</h4>
4859 <ul class="list">
4860     <li> <b>*</b> any element</li>
4861     <li> <b>E</b> an element with the tag E</li>
4862     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4863     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4864     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4865     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4866 </ul>
4867 <h4>Attribute Selectors:</h4>
4868 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4869 <ul class="list">
4870     <li> <b>E[foo]</b> has an attribute "foo"</li>
4871     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4872     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4873     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4874     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4875     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4876     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4877 </ul>
4878 <h4>Pseudo Classes:</h4>
4879 <ul class="list">
4880     <li> <b>E:first-child</b> E is the first child of its parent</li>
4881     <li> <b>E:last-child</b> E is the last child of its parent</li>
4882     <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>
4883     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4884     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4885     <li> <b>E:only-child</b> E is the only child of its parent</li>
4886     <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>
4887     <li> <b>E:first</b> the first E in the resultset</li>
4888     <li> <b>E:last</b> the last E in the resultset</li>
4889     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4890     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4891     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4892     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4893     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4894     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4895     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4896     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4897     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4898 </ul>
4899 <h4>CSS Value Selectors:</h4>
4900 <ul class="list">
4901     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4902     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4903     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4904     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4905     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4906     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4907 </ul>
4908  * @singleton
4909  */
4910 Roo.DomQuery = function(){
4911     var cache = {}, simpleCache = {}, valueCache = {};
4912     var nonSpace = /\S/;
4913     var trimRe = /^\s+|\s+$/g;
4914     var tplRe = /\{(\d+)\}/g;
4915     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4916     var tagTokenRe = /^(#)?([\w-\*]+)/;
4917     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4918
4919     function child(p, index){
4920         var i = 0;
4921         var n = p.firstChild;
4922         while(n){
4923             if(n.nodeType == 1){
4924                if(++i == index){
4925                    return n;
4926                }
4927             }
4928             n = n.nextSibling;
4929         }
4930         return null;
4931     };
4932
4933     function next(n){
4934         while((n = n.nextSibling) && n.nodeType != 1);
4935         return n;
4936     };
4937
4938     function prev(n){
4939         while((n = n.previousSibling) && n.nodeType != 1);
4940         return n;
4941     };
4942
4943     function children(d){
4944         var n = d.firstChild, ni = -1;
4945             while(n){
4946                 var nx = n.nextSibling;
4947                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4948                     d.removeChild(n);
4949                 }else{
4950                     n.nodeIndex = ++ni;
4951                 }
4952                 n = nx;
4953             }
4954             return this;
4955         };
4956
4957     function byClassName(c, a, v){
4958         if(!v){
4959             return c;
4960         }
4961         var r = [], ri = -1, cn;
4962         for(var i = 0, ci; ci = c[i]; i++){
4963             if((' '+ci.className+' ').indexOf(v) != -1){
4964                 r[++ri] = ci;
4965             }
4966         }
4967         return r;
4968     };
4969
4970     function attrValue(n, attr){
4971         if(!n.tagName && typeof n.length != "undefined"){
4972             n = n[0];
4973         }
4974         if(!n){
4975             return null;
4976         }
4977         if(attr == "for"){
4978             return n.htmlFor;
4979         }
4980         if(attr == "class" || attr == "className"){
4981             return n.className;
4982         }
4983         return n.getAttribute(attr) || n[attr];
4984
4985     };
4986
4987     function getNodes(ns, mode, tagName){
4988         var result = [], ri = -1, cs;
4989         if(!ns){
4990             return result;
4991         }
4992         tagName = tagName || "*";
4993         if(typeof ns.getElementsByTagName != "undefined"){
4994             ns = [ns];
4995         }
4996         if(!mode){
4997             for(var i = 0, ni; ni = ns[i]; i++){
4998                 cs = ni.getElementsByTagName(tagName);
4999                 for(var j = 0, ci; ci = cs[j]; j++){
5000                     result[++ri] = ci;
5001                 }
5002             }
5003         }else if(mode == "/" || mode == ">"){
5004             var utag = tagName.toUpperCase();
5005             for(var i = 0, ni, cn; ni = ns[i]; i++){
5006                 cn = ni.children || ni.childNodes;
5007                 for(var j = 0, cj; cj = cn[j]; j++){
5008                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5009                         result[++ri] = cj;
5010                     }
5011                 }
5012             }
5013         }else if(mode == "+"){
5014             var utag = tagName.toUpperCase();
5015             for(var i = 0, n; n = ns[i]; i++){
5016                 while((n = n.nextSibling) && n.nodeType != 1);
5017                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5018                     result[++ri] = n;
5019                 }
5020             }
5021         }else if(mode == "~"){
5022             for(var i = 0, n; n = ns[i]; i++){
5023                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5024                 if(n){
5025                     result[++ri] = n;
5026                 }
5027             }
5028         }
5029         return result;
5030     };
5031
5032     function concat(a, b){
5033         if(b.slice){
5034             return a.concat(b);
5035         }
5036         for(var i = 0, l = b.length; i < l; i++){
5037             a[a.length] = b[i];
5038         }
5039         return a;
5040     }
5041
5042     function byTag(cs, tagName){
5043         if(cs.tagName || cs == document){
5044             cs = [cs];
5045         }
5046         if(!tagName){
5047             return cs;
5048         }
5049         var r = [], ri = -1;
5050         tagName = tagName.toLowerCase();
5051         for(var i = 0, ci; ci = cs[i]; i++){
5052             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5053                 r[++ri] = ci;
5054             }
5055         }
5056         return r;
5057     };
5058
5059     function byId(cs, attr, id){
5060         if(cs.tagName || cs == document){
5061             cs = [cs];
5062         }
5063         if(!id){
5064             return cs;
5065         }
5066         var r = [], ri = -1;
5067         for(var i = 0,ci; ci = cs[i]; i++){
5068             if(ci && ci.id == id){
5069                 r[++ri] = ci;
5070                 return r;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function byAttribute(cs, attr, value, op, custom){
5077         var r = [], ri = -1, st = custom=="{";
5078         var f = Roo.DomQuery.operators[op];
5079         for(var i = 0, ci; ci = cs[i]; i++){
5080             var a;
5081             if(st){
5082                 a = Roo.DomQuery.getStyle(ci, attr);
5083             }
5084             else if(attr == "class" || attr == "className"){
5085                 a = ci.className;
5086             }else if(attr == "for"){
5087                 a = ci.htmlFor;
5088             }else if(attr == "href"){
5089                 a = ci.getAttribute("href", 2);
5090             }else{
5091                 a = ci.getAttribute(attr);
5092             }
5093             if((f && f(a, value)) || (!f && a)){
5094                 r[++ri] = ci;
5095             }
5096         }
5097         return r;
5098     };
5099
5100     function byPseudo(cs, name, value){
5101         return Roo.DomQuery.pseudos[name](cs, value);
5102     };
5103
5104     // This is for IE MSXML which does not support expandos.
5105     // IE runs the same speed using setAttribute, however FF slows way down
5106     // and Safari completely fails so they need to continue to use expandos.
5107     var isIE = window.ActiveXObject ? true : false;
5108
5109     // this eval is stop the compressor from
5110     // renaming the variable to something shorter
5111     
5112     /** eval:var:batch */
5113     var batch = 30803; 
5114
5115     var key = 30803;
5116
5117     function nodupIEXml(cs){
5118         var d = ++key;
5119         cs[0].setAttribute("_nodup", d);
5120         var r = [cs[0]];
5121         for(var i = 1, len = cs.length; i < len; i++){
5122             var c = cs[i];
5123             if(!c.getAttribute("_nodup") != d){
5124                 c.setAttribute("_nodup", d);
5125                 r[r.length] = c;
5126             }
5127         }
5128         for(var i = 0, len = cs.length; i < len; i++){
5129             cs[i].removeAttribute("_nodup");
5130         }
5131         return r;
5132     }
5133
5134     function nodup(cs){
5135         if(!cs){
5136             return [];
5137         }
5138         var len = cs.length, c, i, r = cs, cj, ri = -1;
5139         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5140             return cs;
5141         }
5142         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5143             return nodupIEXml(cs);
5144         }
5145         var d = ++key;
5146         cs[0]._nodup = d;
5147         for(i = 1; c = cs[i]; i++){
5148             if(c._nodup != d){
5149                 c._nodup = d;
5150             }else{
5151                 r = [];
5152                 for(var j = 0; j < i; j++){
5153                     r[++ri] = cs[j];
5154                 }
5155                 for(j = i+1; cj = cs[j]; j++){
5156                     if(cj._nodup != d){
5157                         cj._nodup = d;
5158                         r[++ri] = cj;
5159                     }
5160                 }
5161                 return r;
5162             }
5163         }
5164         return r;
5165     }
5166
5167     function quickDiffIEXml(c1, c2){
5168         var d = ++key;
5169         for(var i = 0, len = c1.length; i < len; i++){
5170             c1[i].setAttribute("_qdiff", d);
5171         }
5172         var r = [];
5173         for(var i = 0, len = c2.length; i < len; i++){
5174             if(c2[i].getAttribute("_qdiff") != d){
5175                 r[r.length] = c2[i];
5176             }
5177         }
5178         for(var i = 0, len = c1.length; i < len; i++){
5179            c1[i].removeAttribute("_qdiff");
5180         }
5181         return r;
5182     }
5183
5184     function quickDiff(c1, c2){
5185         var len1 = c1.length;
5186         if(!len1){
5187             return c2;
5188         }
5189         if(isIE && c1[0].selectSingleNode){
5190             return quickDiffIEXml(c1, c2);
5191         }
5192         var d = ++key;
5193         for(var i = 0; i < len1; i++){
5194             c1[i]._qdiff = d;
5195         }
5196         var r = [];
5197         for(var i = 0, len = c2.length; i < len; i++){
5198             if(c2[i]._qdiff != d){
5199                 r[r.length] = c2[i];
5200             }
5201         }
5202         return r;
5203     }
5204
5205     function quickId(ns, mode, root, id){
5206         if(ns == root){
5207            var d = root.ownerDocument || root;
5208            return d.getElementById(id);
5209         }
5210         ns = getNodes(ns, mode, "*");
5211         return byId(ns, null, id);
5212     }
5213
5214     return {
5215         getStyle : function(el, name){
5216             return Roo.fly(el).getStyle(name);
5217         },
5218         /**
5219          * Compiles a selector/xpath query into a reusable function. The returned function
5220          * takes one parameter "root" (optional), which is the context node from where the query should start.
5221          * @param {String} selector The selector/xpath query
5222          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5223          * @return {Function}
5224          */
5225         compile : function(path, type){
5226             type = type || "select";
5227             
5228             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5229             var q = path, mode, lq;
5230             var tk = Roo.DomQuery.matchers;
5231             var tklen = tk.length;
5232             var mm;
5233
5234             // accept leading mode switch
5235             var lmode = q.match(modeRe);
5236             if(lmode && lmode[1]){
5237                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5238                 q = q.replace(lmode[1], "");
5239             }
5240             // strip leading slashes
5241             while(path.substr(0, 1)=="/"){
5242                 path = path.substr(1);
5243             }
5244
5245             while(q && lq != q){
5246                 lq = q;
5247                 var tm = q.match(tagTokenRe);
5248                 if(type == "select"){
5249                     if(tm){
5250                         if(tm[1] == "#"){
5251                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5252                         }else{
5253                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5254                         }
5255                         q = q.replace(tm[0], "");
5256                     }else if(q.substr(0, 1) != '@'){
5257                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5258                     }
5259                 }else{
5260                     if(tm){
5261                         if(tm[1] == "#"){
5262                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5263                         }else{
5264                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5265                         }
5266                         q = q.replace(tm[0], "");
5267                     }
5268                 }
5269                 while(!(mm = q.match(modeRe))){
5270                     var matched = false;
5271                     for(var j = 0; j < tklen; j++){
5272                         var t = tk[j];
5273                         var m = q.match(t.re);
5274                         if(m){
5275                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5276                                                     return m[i];
5277                                                 });
5278                             q = q.replace(m[0], "");
5279                             matched = true;
5280                             break;
5281                         }
5282                     }
5283                     // prevent infinite loop on bad selector
5284                     if(!matched){
5285                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5286                     }
5287                 }
5288                 if(mm[1]){
5289                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5290                     q = q.replace(mm[1], "");
5291                 }
5292             }
5293             fn[fn.length] = "return nodup(n);\n}";
5294             
5295              /** 
5296               * list of variables that need from compression as they are used by eval.
5297              *  eval:var:batch 
5298              *  eval:var:nodup
5299              *  eval:var:byTag
5300              *  eval:var:ById
5301              *  eval:var:getNodes
5302              *  eval:var:quickId
5303              *  eval:var:mode
5304              *  eval:var:root
5305              *  eval:var:n
5306              *  eval:var:byClassName
5307              *  eval:var:byPseudo
5308              *  eval:var:byAttribute
5309              *  eval:var:attrValue
5310              * 
5311              **/ 
5312             eval(fn.join(""));
5313             return f;
5314         },
5315
5316         /**
5317          * Selects a group of elements.
5318          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5319          * @param {Node} root (optional) The start of the query (defaults to document).
5320          * @return {Array}
5321          */
5322         select : function(path, root, type){
5323             if(!root || root == document){
5324                 root = document;
5325             }
5326             if(typeof root == "string"){
5327                 root = document.getElementById(root);
5328             }
5329             var paths = path.split(",");
5330             var results = [];
5331             for(var i = 0, len = paths.length; i < len; i++){
5332                 var p = paths[i].replace(trimRe, "");
5333                 if(!cache[p]){
5334                     cache[p] = Roo.DomQuery.compile(p);
5335                     if(!cache[p]){
5336                         throw p + " is not a valid selector";
5337                     }
5338                 }
5339                 var result = cache[p](root);
5340                 if(result && result != document){
5341                     results = results.concat(result);
5342                 }
5343             }
5344             if(paths.length > 1){
5345                 return nodup(results);
5346             }
5347             return results;
5348         },
5349
5350         /**
5351          * Selects a single element.
5352          * @param {String} selector The selector/xpath query
5353          * @param {Node} root (optional) The start of the query (defaults to document).
5354          * @return {Element}
5355          */
5356         selectNode : function(path, root){
5357             return Roo.DomQuery.select(path, root)[0];
5358         },
5359
5360         /**
5361          * Selects the value of a node, optionally replacing null with the defaultValue.
5362          * @param {String} selector The selector/xpath query
5363          * @param {Node} root (optional) The start of the query (defaults to document).
5364          * @param {String} defaultValue
5365          */
5366         selectValue : function(path, root, defaultValue){
5367             path = path.replace(trimRe, "");
5368             if(!valueCache[path]){
5369                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5370             }
5371             var n = valueCache[path](root);
5372             n = n[0] ? n[0] : n;
5373             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5374             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5375         },
5376
5377         /**
5378          * Selects the value of a node, parsing integers and floats.
5379          * @param {String} selector The selector/xpath query
5380          * @param {Node} root (optional) The start of the query (defaults to document).
5381          * @param {Number} defaultValue
5382          * @return {Number}
5383          */
5384         selectNumber : function(path, root, defaultValue){
5385             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5386             return parseFloat(v);
5387         },
5388
5389         /**
5390          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5391          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5392          * @param {String} selector The simple selector to test
5393          * @return {Boolean}
5394          */
5395         is : function(el, ss){
5396             if(typeof el == "string"){
5397                 el = document.getElementById(el);
5398             }
5399             var isArray = (el instanceof Array);
5400             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5401             return isArray ? (result.length == el.length) : (result.length > 0);
5402         },
5403
5404         /**
5405          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5406          * @param {Array} el An array of elements to filter
5407          * @param {String} selector The simple selector to test
5408          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5409          * the selector instead of the ones that match
5410          * @return {Array}
5411          */
5412         filter : function(els, ss, nonMatches){
5413             ss = ss.replace(trimRe, "");
5414             if(!simpleCache[ss]){
5415                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5416             }
5417             var result = simpleCache[ss](els);
5418             return nonMatches ? quickDiff(result, els) : result;
5419         },
5420
5421         /**
5422          * Collection of matching regular expressions and code snippets.
5423          */
5424         matchers : [{
5425                 re: /^\.([\w-]+)/,
5426                 select: 'n = byClassName(n, null, " {1} ");'
5427             }, {
5428                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5429                 select: 'n = byPseudo(n, "{1}", "{2}");'
5430             },{
5431                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5432                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5433             }, {
5434                 re: /^#([\w-]+)/,
5435                 select: 'n = byId(n, null, "{1}");'
5436             },{
5437                 re: /^@([\w-]+)/,
5438                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5439             }
5440         ],
5441
5442         /**
5443          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5444          * 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;.
5445          */
5446         operators : {
5447             "=" : function(a, v){
5448                 return a == v;
5449             },
5450             "!=" : function(a, v){
5451                 return a != v;
5452             },
5453             "^=" : function(a, v){
5454                 return a && a.substr(0, v.length) == v;
5455             },
5456             "$=" : function(a, v){
5457                 return a && a.substr(a.length-v.length) == v;
5458             },
5459             "*=" : function(a, v){
5460                 return a && a.indexOf(v) !== -1;
5461             },
5462             "%=" : function(a, v){
5463                 return (a % v) == 0;
5464             },
5465             "|=" : function(a, v){
5466                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5467             },
5468             "~=" : function(a, v){
5469                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5470             }
5471         },
5472
5473         /**
5474          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5475          * and the argument (if any) supplied in the selector.
5476          */
5477         pseudos : {
5478             "first-child" : function(c){
5479                 var r = [], ri = -1, n;
5480                 for(var i = 0, ci; ci = n = c[i]; i++){
5481                     while((n = n.previousSibling) && n.nodeType != 1);
5482                     if(!n){
5483                         r[++ri] = ci;
5484                     }
5485                 }
5486                 return r;
5487             },
5488
5489             "last-child" : function(c){
5490                 var r = [], ri = -1, n;
5491                 for(var i = 0, ci; ci = n = c[i]; i++){
5492                     while((n = n.nextSibling) && n.nodeType != 1);
5493                     if(!n){
5494                         r[++ri] = ci;
5495                     }
5496                 }
5497                 return r;
5498             },
5499
5500             "nth-child" : function(c, a) {
5501                 var r = [], ri = -1;
5502                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5503                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5504                 for(var i = 0, n; n = c[i]; i++){
5505                     var pn = n.parentNode;
5506                     if (batch != pn._batch) {
5507                         var j = 0;
5508                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5509                             if(cn.nodeType == 1){
5510                                cn.nodeIndex = ++j;
5511                             }
5512                         }
5513                         pn._batch = batch;
5514                     }
5515                     if (f == 1) {
5516                         if (l == 0 || n.nodeIndex == l){
5517                             r[++ri] = n;
5518                         }
5519                     } else if ((n.nodeIndex + l) % f == 0){
5520                         r[++ri] = n;
5521                     }
5522                 }
5523
5524                 return r;
5525             },
5526
5527             "only-child" : function(c){
5528                 var r = [], ri = -1;;
5529                 for(var i = 0, ci; ci = c[i]; i++){
5530                     if(!prev(ci) && !next(ci)){
5531                         r[++ri] = ci;
5532                     }
5533                 }
5534                 return r;
5535             },
5536
5537             "empty" : function(c){
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     var cns = ci.childNodes, j = 0, cn, empty = true;
5541                     while(cn = cns[j]){
5542                         ++j;
5543                         if(cn.nodeType == 1 || cn.nodeType == 3){
5544                             empty = false;
5545                             break;
5546                         }
5547                     }
5548                     if(empty){
5549                         r[++ri] = ci;
5550                     }
5551                 }
5552                 return r;
5553             },
5554
5555             "contains" : function(c, v){
5556                 var r = [], ri = -1;
5557                 for(var i = 0, ci; ci = c[i]; i++){
5558                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5559                         r[++ri] = ci;
5560                     }
5561                 }
5562                 return r;
5563             },
5564
5565             "nodeValue" : function(c, v){
5566                 var r = [], ri = -1;
5567                 for(var i = 0, ci; ci = c[i]; i++){
5568                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5569                         r[++ri] = ci;
5570                     }
5571                 }
5572                 return r;
5573             },
5574
5575             "checked" : function(c){
5576                 var r = [], ri = -1;
5577                 for(var i = 0, ci; ci = c[i]; i++){
5578                     if(ci.checked == true){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "not" : function(c, ss){
5586                 return Roo.DomQuery.filter(c, ss, true);
5587             },
5588
5589             "odd" : function(c){
5590                 return this["nth-child"](c, "odd");
5591             },
5592
5593             "even" : function(c){
5594                 return this["nth-child"](c, "even");
5595             },
5596
5597             "nth" : function(c, a){
5598                 return c[a-1] || [];
5599             },
5600
5601             "first" : function(c){
5602                 return c[0] || [];
5603             },
5604
5605             "last" : function(c){
5606                 return c[c.length-1] || [];
5607             },
5608
5609             "has" : function(c, ss){
5610                 var s = Roo.DomQuery.select;
5611                 var r = [], ri = -1;
5612                 for(var i = 0, ci; ci = c[i]; i++){
5613                     if(s(ss, ci).length > 0){
5614                         r[++ri] = ci;
5615                     }
5616                 }
5617                 return r;
5618             },
5619
5620             "next" : function(c, ss){
5621                 var is = Roo.DomQuery.is;
5622                 var r = [], ri = -1;
5623                 for(var i = 0, ci; ci = c[i]; i++){
5624                     var n = next(ci);
5625                     if(n && is(n, ss)){
5626                         r[++ri] = ci;
5627                     }
5628                 }
5629                 return r;
5630             },
5631
5632             "prev" : function(c, ss){
5633                 var is = Roo.DomQuery.is;
5634                 var r = [], ri = -1;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     var n = prev(ci);
5637                     if(n && is(n, ss)){
5638                         r[++ri] = ci;
5639                     }
5640                 }
5641                 return r;
5642             }
5643         }
5644     };
5645 }();
5646
5647 /**
5648  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5649  * @param {String} path The selector/xpath query
5650  * @param {Node} root (optional) The start of the query (defaults to document).
5651  * @return {Array}
5652  * @member Roo
5653  * @method query
5654  */
5655 Roo.query = Roo.DomQuery.select;
5656 /*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.util.Observable
5669  * Base class that provides a common interface for publishing events. Subclasses are expected to
5670  * to have a property "events" with all the events defined.<br>
5671  * For example:
5672  * <pre><code>
5673  Employee = function(name){
5674     this.name = name;
5675     this.addEvents({
5676         "fired" : true,
5677         "quit" : true
5678     });
5679  }
5680  Roo.extend(Employee, Roo.util.Observable);
5681 </code></pre>
5682  * @param {Object} config properties to use (incuding events / listeners)
5683  */
5684
5685 Roo.util.Observable = function(cfg){
5686     
5687     cfg = cfg|| {};
5688     this.addEvents(cfg.events || {});
5689     if (cfg.events) {
5690         delete cfg.events; // make sure
5691     }
5692      
5693     Roo.apply(this, cfg);
5694     
5695     if(this.listeners){
5696         this.on(this.listeners);
5697         delete this.listeners;
5698     }
5699 };
5700 Roo.util.Observable.prototype = {
5701     /** 
5702  * @cfg {Object} listeners  list of events and functions to call for this object, 
5703  * For example :
5704  * <pre><code>
5705     listeners :  { 
5706        'click' : function(e) {
5707            ..... 
5708         } ,
5709         .... 
5710     } 
5711   </code></pre>
5712  */
5713     
5714     
5715     /**
5716      * Fires the specified event with the passed parameters (minus the event name).
5717      * @param {String} eventName
5718      * @param {Object...} args Variable number of parameters are passed to handlers
5719      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5720      */
5721     fireEvent : function(){
5722         var ce = this.events[arguments[0].toLowerCase()];
5723         if(typeof ce == "object"){
5724             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5725         }else{
5726             return true;
5727         }
5728     },
5729
5730     // private
5731     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5732
5733     /**
5734      * Appends an event handler to this component
5735      * @param {String}   eventName The type of event to listen for
5736      * @param {Function} handler The method the event invokes
5737      * @param {Object}   scope (optional) The scope in which to execute the handler
5738      * function. The handler function's "this" context.
5739      * @param {Object}   options (optional) An object containing handler configuration
5740      * properties. This may contain any of the following properties:<ul>
5741      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5742      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5743      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5744      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5745      * by the specified number of milliseconds. If the event fires again within that time, the original
5746      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5747      * </ul><br>
5748      * <p>
5749      * <b>Combining Options</b><br>
5750      * Using the options argument, it is possible to combine different types of listeners:<br>
5751      * <br>
5752      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5753                 <pre><code>
5754                 el.on('click', this.onClick, this, {
5755                         single: true,
5756                 delay: 100,
5757                 forumId: 4
5758                 });
5759                 </code></pre>
5760      * <p>
5761      * <b>Attaching multiple handlers in 1 call</b><br>
5762      * The method also allows for a single argument to be passed which is a config object containing properties
5763      * which specify multiple handlers.
5764      * <pre><code>
5765                 el.on({
5766                         'click': {
5767                         fn: this.onClick,
5768                         scope: this,
5769                         delay: 100
5770                 }, 
5771                 'mouseover': {
5772                         fn: this.onMouseOver,
5773                         scope: this
5774                 },
5775                 'mouseout': {
5776                         fn: this.onMouseOut,
5777                         scope: this
5778                 }
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * Or a shorthand syntax which passes the same scope object to all handlers:
5783         <pre><code>
5784                 el.on({
5785                         'click': this.onClick,
5786                 'mouseover': this.onMouseOver,
5787                 'mouseout': this.onMouseOut,
5788                 scope: this
5789                 });
5790                 </code></pre>
5791      */
5792     addListener : function(eventName, fn, scope, o){
5793         if(typeof eventName == "object"){
5794             o = eventName;
5795             for(var e in o){
5796                 if(this.filterOptRe.test(e)){
5797                     continue;
5798                 }
5799                 if(typeof o[e] == "function"){
5800                     // shared options
5801                     this.addListener(e, o[e], o.scope,  o);
5802                 }else{
5803                     // individual options
5804                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5805                 }
5806             }
5807             return;
5808         }
5809         o = (!o || typeof o == "boolean") ? {} : o;
5810         eventName = eventName.toLowerCase();
5811         var ce = this.events[eventName] || true;
5812         if(typeof ce == "boolean"){
5813             ce = new Roo.util.Event(this, eventName);
5814             this.events[eventName] = ce;
5815         }
5816         ce.addListener(fn, scope, o);
5817     },
5818
5819     /**
5820      * Removes a listener
5821      * @param {String}   eventName     The type of event to listen for
5822      * @param {Function} handler        The handler to remove
5823      * @param {Object}   scope  (optional) The scope (this object) for the handler
5824      */
5825     removeListener : function(eventName, fn, scope){
5826         var ce = this.events[eventName.toLowerCase()];
5827         if(typeof ce == "object"){
5828             ce.removeListener(fn, scope);
5829         }
5830     },
5831
5832     /**
5833      * Removes all listeners for this object
5834      */
5835     purgeListeners : function(){
5836         for(var evt in this.events){
5837             if(typeof this.events[evt] == "object"){
5838                  this.events[evt].clearListeners();
5839             }
5840         }
5841     },
5842
5843     relayEvents : function(o, events){
5844         var createHandler = function(ename){
5845             return function(){
5846                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5847             };
5848         };
5849         for(var i = 0, len = events.length; i < len; i++){
5850             var ename = events[i];
5851             if(!this.events[ename]){ this.events[ename] = true; };
5852             o.on(ename, createHandler(ename), this);
5853         }
5854     },
5855
5856     /**
5857      * Used to define events on this Observable
5858      * @param {Object} object The object with the events defined
5859      */
5860     addEvents : function(o){
5861         if(!this.events){
5862             this.events = {};
5863         }
5864         Roo.applyIf(this.events, o);
5865     },
5866
5867     /**
5868      * Checks to see if this object has any listeners for a specified event
5869      * @param {String} eventName The name of the event to check for
5870      * @return {Boolean} True if the event is being listened for, else false
5871      */
5872     hasListener : function(eventName){
5873         var e = this.events[eventName];
5874         return typeof e == "object" && e.listeners.length > 0;
5875     }
5876 };
5877 /**
5878  * Appends an event handler to this element (shorthand for addListener)
5879  * @param {String}   eventName     The type of event to listen for
5880  * @param {Function} handler        The method the event invokes
5881  * @param {Object}   scope (optional) The scope in which to execute the handler
5882  * function. The handler function's "this" context.
5883  * @param {Object}   options  (optional)
5884  * @method
5885  */
5886 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5887 /**
5888  * Removes a listener (shorthand for removeListener)
5889  * @param {String}   eventName     The type of event to listen for
5890  * @param {Function} handler        The handler to remove
5891  * @param {Object}   scope  (optional) The scope (this object) for the handler
5892  * @method
5893  */
5894 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5895
5896 /**
5897  * Starts capture on the specified Observable. All events will be passed
5898  * to the supplied function with the event name + standard signature of the event
5899  * <b>before</b> the event is fired. If the supplied function returns false,
5900  * the event will not fire.
5901  * @param {Observable} o The Observable to capture
5902  * @param {Function} fn The function to call
5903  * @param {Object} scope (optional) The scope (this object) for the fn
5904  * @static
5905  */
5906 Roo.util.Observable.capture = function(o, fn, scope){
5907     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5908 };
5909
5910 /**
5911  * Removes <b>all</b> added captures from the Observable.
5912  * @param {Observable} o The Observable to release
5913  * @static
5914  */
5915 Roo.util.Observable.releaseCapture = function(o){
5916     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5917 };
5918
5919 (function(){
5920
5921     var createBuffered = function(h, o, scope){
5922         var task = new Roo.util.DelayedTask();
5923         return function(){
5924             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5925         };
5926     };
5927
5928     var createSingle = function(h, e, fn, scope){
5929         return function(){
5930             e.removeListener(fn, scope);
5931             return h.apply(scope, arguments);
5932         };
5933     };
5934
5935     var createDelayed = function(h, o, scope){
5936         return function(){
5937             var args = Array.prototype.slice.call(arguments, 0);
5938             setTimeout(function(){
5939                 h.apply(scope, args);
5940             }, o.delay || 10);
5941         };
5942     };
5943
5944     Roo.util.Event = function(obj, name){
5945         this.name = name;
5946         this.obj = obj;
5947         this.listeners = [];
5948     };
5949
5950     Roo.util.Event.prototype = {
5951         addListener : function(fn, scope, options){
5952             var o = options || {};
5953             scope = scope || this.obj;
5954             if(!this.isListening(fn, scope)){
5955                 var l = {fn: fn, scope: scope, options: o};
5956                 var h = fn;
5957                 if(o.delay){
5958                     h = createDelayed(h, o, scope);
5959                 }
5960                 if(o.single){
5961                     h = createSingle(h, this, fn, scope);
5962                 }
5963                 if(o.buffer){
5964                     h = createBuffered(h, o, scope);
5965                 }
5966                 l.fireFn = h;
5967                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5968                     this.listeners.push(l);
5969                 }else{
5970                     this.listeners = this.listeners.slice(0);
5971                     this.listeners.push(l);
5972                 }
5973             }
5974         },
5975
5976         findListener : function(fn, scope){
5977             scope = scope || this.obj;
5978             var ls = this.listeners;
5979             for(var i = 0, len = ls.length; i < len; i++){
5980                 var l = ls[i];
5981                 if(l.fn == fn && l.scope == scope){
5982                     return i;
5983                 }
5984             }
5985             return -1;
5986         },
5987
5988         isListening : function(fn, scope){
5989             return this.findListener(fn, scope) != -1;
5990         },
5991
5992         removeListener : function(fn, scope){
5993             var index;
5994             if((index = this.findListener(fn, scope)) != -1){
5995                 if(!this.firing){
5996                     this.listeners.splice(index, 1);
5997                 }else{
5998                     this.listeners = this.listeners.slice(0);
5999                     this.listeners.splice(index, 1);
6000                 }
6001                 return true;
6002             }
6003             return false;
6004         },
6005
6006         clearListeners : function(){
6007             this.listeners = [];
6008         },
6009
6010         fire : function(){
6011             var ls = this.listeners, scope, len = ls.length;
6012             if(len > 0){
6013                 this.firing = true;
6014                 var args = Array.prototype.slice.call(arguments, 0);
6015                 for(var i = 0; i < len; i++){
6016                     var l = ls[i];
6017                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6018                         this.firing = false;
6019                         return false;
6020                     }
6021                 }
6022                 this.firing = false;
6023             }
6024             return true;
6025         }
6026     };
6027 })();/*
6028  * Based on:
6029  * Ext JS Library 1.1.1
6030  * Copyright(c) 2006-2007, Ext JS, LLC.
6031  *
6032  * Originally Released Under LGPL - original licence link has changed is not relivant.
6033  *
6034  * Fork - LGPL
6035  * <script type="text/javascript">
6036  */
6037
6038 /**
6039  * @class Roo.EventManager
6040  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6041  * several useful events directly.
6042  * See {@link Roo.EventObject} for more details on normalized event objects.
6043  * @singleton
6044  */
6045 Roo.EventManager = function(){
6046     var docReadyEvent, docReadyProcId, docReadyState = false;
6047     var resizeEvent, resizeTask, textEvent, textSize;
6048     var E = Roo.lib.Event;
6049     var D = Roo.lib.Dom;
6050
6051     
6052     
6053
6054     var fireDocReady = function(){
6055         if(!docReadyState){
6056             docReadyState = true;
6057             Roo.isReady = true;
6058             if(docReadyProcId){
6059                 clearInterval(docReadyProcId);
6060             }
6061             if(Roo.isGecko || Roo.isOpera) {
6062                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6063             }
6064             if(Roo.isIE){
6065                 var defer = document.getElementById("ie-deferred-loader");
6066                 if(defer){
6067                     defer.onreadystatechange = null;
6068                     defer.parentNode.removeChild(defer);
6069                 }
6070             }
6071             if(docReadyEvent){
6072                 docReadyEvent.fire();
6073                 docReadyEvent.clearListeners();
6074             }
6075         }
6076     };
6077     
6078     var initDocReady = function(){
6079         docReadyEvent = new Roo.util.Event();
6080         if(Roo.isGecko || Roo.isOpera) {
6081             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6082         }else if(Roo.isIE){
6083             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6084             var defer = document.getElementById("ie-deferred-loader");
6085             defer.onreadystatechange = function(){
6086                 if(this.readyState == "complete"){
6087                     fireDocReady();
6088                 }
6089             };
6090         }else if(Roo.isSafari){ 
6091             docReadyProcId = setInterval(function(){
6092                 var rs = document.readyState;
6093                 if(rs == "complete") {
6094                     fireDocReady();     
6095                  }
6096             }, 10);
6097         }
6098         // no matter what, make sure it fires on load
6099         E.on(window, "load", fireDocReady);
6100     };
6101
6102     var createBuffered = function(h, o){
6103         var task = new Roo.util.DelayedTask(h);
6104         return function(e){
6105             // create new event object impl so new events don't wipe out properties
6106             e = new Roo.EventObjectImpl(e);
6107             task.delay(o.buffer, h, null, [e]);
6108         };
6109     };
6110
6111     var createSingle = function(h, el, ename, fn){
6112         return function(e){
6113             Roo.EventManager.removeListener(el, ename, fn);
6114             h(e);
6115         };
6116     };
6117
6118     var createDelayed = function(h, o){
6119         return function(e){
6120             // create new event object impl so new events don't wipe out properties
6121             e = new Roo.EventObjectImpl(e);
6122             setTimeout(function(){
6123                 h(e);
6124             }, o.delay || 10);
6125         };
6126     };
6127     var transitionEndVal = false;
6128     
6129     var transitionEnd = function()
6130     {
6131         if (transitionEndVal) {
6132             return transitionEndVal;
6133         }
6134         var el = document.createElement('div');
6135
6136         var transEndEventNames = {
6137             WebkitTransition : 'webkitTransitionEnd',
6138             MozTransition    : 'transitionend',
6139             OTransition      : 'oTransitionEnd otransitionend',
6140             transition       : 'transitionend'
6141         };
6142     
6143         for (var name in transEndEventNames) {
6144             if (el.style[name] !== undefined) {
6145                 transitionEndVal = transEndEventNames[name];
6146                 return  transitionEndVal ;
6147             }
6148         }
6149     }
6150     
6151
6152     var listen = function(element, ename, opt, fn, scope){
6153         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6154         fn = fn || o.fn; scope = scope || o.scope;
6155         var el = Roo.getDom(element);
6156         
6157         
6158         if(!el){
6159             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6160         }
6161         
6162         if (ename == 'transitionend') {
6163             ename = transitionEnd();
6164         }
6165         var h = function(e){
6166             e = Roo.EventObject.setEvent(e);
6167             var t;
6168             if(o.delegate){
6169                 t = e.getTarget(o.delegate, el);
6170                 if(!t){
6171                     return;
6172                 }
6173             }else{
6174                 t = e.target;
6175             }
6176             if(o.stopEvent === true){
6177                 e.stopEvent();
6178             }
6179             if(o.preventDefault === true){
6180                e.preventDefault();
6181             }
6182             if(o.stopPropagation === true){
6183                 e.stopPropagation();
6184             }
6185
6186             if(o.normalized === false){
6187                 e = e.browserEvent;
6188             }
6189
6190             fn.call(scope || el, e, t, o);
6191         };
6192         if(o.delay){
6193             h = createDelayed(h, o);
6194         }
6195         if(o.single){
6196             h = createSingle(h, el, ename, fn);
6197         }
6198         if(o.buffer){
6199             h = createBuffered(h, o);
6200         }
6201         fn._handlers = fn._handlers || [];
6202         
6203         
6204         fn._handlers.push([Roo.id(el), ename, h]);
6205         
6206         
6207          
6208         E.on(el, ename, h);
6209         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6210             el.addEventListener("DOMMouseScroll", h, false);
6211             E.on(window, 'unload', function(){
6212                 el.removeEventListener("DOMMouseScroll", h, false);
6213             });
6214         }
6215         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6216             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6217         }
6218         return h;
6219     };
6220
6221     var stopListening = function(el, ename, fn){
6222         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6223         if(hds){
6224             for(var i = 0, len = hds.length; i < len; i++){
6225                 var h = hds[i];
6226                 if(h[0] == id && h[1] == ename){
6227                     hd = h[2];
6228                     hds.splice(i, 1);
6229                     break;
6230                 }
6231             }
6232         }
6233         E.un(el, ename, hd);
6234         el = Roo.getDom(el);
6235         if(ename == "mousewheel" && el.addEventListener){
6236             el.removeEventListener("DOMMouseScroll", hd, false);
6237         }
6238         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6239             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6240         }
6241     };
6242
6243     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6244     
6245     var pub = {
6246         
6247         
6248         /** 
6249          * Fix for doc tools
6250          * @scope Roo.EventManager
6251          */
6252         
6253         
6254         /** 
6255          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6256          * object with a Roo.EventObject
6257          * @param {Function} fn        The method the event invokes
6258          * @param {Object}   scope    An object that becomes the scope of the handler
6259          * @param {boolean}  override If true, the obj passed in becomes
6260          *                             the execution scope of the listener
6261          * @return {Function} The wrapped function
6262          * @deprecated
6263          */
6264         wrap : function(fn, scope, override){
6265             return function(e){
6266                 Roo.EventObject.setEvent(e);
6267                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6268             };
6269         },
6270         
6271         /**
6272      * Appends an event handler to an element (shorthand for addListener)
6273      * @param {String/HTMLElement}   element        The html element or id to assign the
6274      * @param {String}   eventName The type of event to listen for
6275      * @param {Function} handler The method the event invokes
6276      * @param {Object}   scope (optional) The scope in which to execute the handler
6277      * function. The handler function's "this" context.
6278      * @param {Object}   options (optional) An object containing handler configuration
6279      * properties. This may contain any of the following properties:<ul>
6280      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6281      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6282      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6283      * <li>preventDefault {Boolean} True to prevent the default action</li>
6284      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6285      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6286      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6287      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6288      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6289      * by the specified number of milliseconds. If the event fires again within that time, the original
6290      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6291      * </ul><br>
6292      * <p>
6293      * <b>Combining Options</b><br>
6294      * Using the options argument, it is possible to combine different types of listeners:<br>
6295      * <br>
6296      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6297      * Code:<pre><code>
6298 el.on('click', this.onClick, this, {
6299     single: true,
6300     delay: 100,
6301     stopEvent : true,
6302     forumId: 4
6303 });</code></pre>
6304      * <p>
6305      * <b>Attaching multiple handlers in 1 call</b><br>
6306       * The method also allows for a single argument to be passed which is a config object containing properties
6307      * which specify multiple handlers.
6308      * <p>
6309      * Code:<pre><code>
6310 el.on({
6311     'click' : {
6312         fn: this.onClick
6313         scope: this,
6314         delay: 100
6315     },
6316     'mouseover' : {
6317         fn: this.onMouseOver
6318         scope: this
6319     },
6320     'mouseout' : {
6321         fn: this.onMouseOut
6322         scope: this
6323     }
6324 });</code></pre>
6325      * <p>
6326      * Or a shorthand syntax:<br>
6327      * Code:<pre><code>
6328 el.on({
6329     'click' : this.onClick,
6330     'mouseover' : this.onMouseOver,
6331     'mouseout' : this.onMouseOut
6332     scope: this
6333 });</code></pre>
6334      */
6335         addListener : function(element, eventName, fn, scope, options){
6336             if(typeof eventName == "object"){
6337                 var o = eventName;
6338                 for(var e in o){
6339                     if(propRe.test(e)){
6340                         continue;
6341                     }
6342                     if(typeof o[e] == "function"){
6343                         // shared options
6344                         listen(element, e, o, o[e], o.scope);
6345                     }else{
6346                         // individual options
6347                         listen(element, e, o[e]);
6348                     }
6349                 }
6350                 return;
6351             }
6352             return listen(element, eventName, options, fn, scope);
6353         },
6354         
6355         /**
6356          * Removes an event handler
6357          *
6358          * @param {String/HTMLElement}   element        The id or html element to remove the 
6359          *                             event from
6360          * @param {String}   eventName     The type of event
6361          * @param {Function} fn
6362          * @return {Boolean} True if a listener was actually removed
6363          */
6364         removeListener : function(element, eventName, fn){
6365             return stopListening(element, eventName, fn);
6366         },
6367         
6368         /**
6369          * Fires when the document is ready (before onload and before images are loaded). Can be 
6370          * accessed shorthanded Roo.onReady().
6371          * @param {Function} fn        The method the event invokes
6372          * @param {Object}   scope    An  object that becomes the scope of the handler
6373          * @param {boolean}  options
6374          */
6375         onDocumentReady : function(fn, scope, options){
6376             if(docReadyState){ // if it already fired
6377                 docReadyEvent.addListener(fn, scope, options);
6378                 docReadyEvent.fire();
6379                 docReadyEvent.clearListeners();
6380                 return;
6381             }
6382             if(!docReadyEvent){
6383                 initDocReady();
6384             }
6385             docReadyEvent.addListener(fn, scope, options);
6386         },
6387         
6388         /**
6389          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6390          * @param {Function} fn        The method the event invokes
6391          * @param {Object}   scope    An object that becomes the scope of the handler
6392          * @param {boolean}  options
6393          */
6394         onWindowResize : function(fn, scope, options){
6395             if(!resizeEvent){
6396                 resizeEvent = new Roo.util.Event();
6397                 resizeTask = new Roo.util.DelayedTask(function(){
6398                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6399                 });
6400                 E.on(window, "resize", function(){
6401                     if(Roo.isIE){
6402                         resizeTask.delay(50);
6403                     }else{
6404                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6405                     }
6406                 });
6407             }
6408             resizeEvent.addListener(fn, scope, options);
6409         },
6410
6411         /**
6412          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6413          * @param {Function} fn        The method the event invokes
6414          * @param {Object}   scope    An object that becomes the scope of the handler
6415          * @param {boolean}  options
6416          */
6417         onTextResize : function(fn, scope, options){
6418             if(!textEvent){
6419                 textEvent = new Roo.util.Event();
6420                 var textEl = new Roo.Element(document.createElement('div'));
6421                 textEl.dom.className = 'x-text-resize';
6422                 textEl.dom.innerHTML = 'X';
6423                 textEl.appendTo(document.body);
6424                 textSize = textEl.dom.offsetHeight;
6425                 setInterval(function(){
6426                     if(textEl.dom.offsetHeight != textSize){
6427                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6428                     }
6429                 }, this.textResizeInterval);
6430             }
6431             textEvent.addListener(fn, scope, options);
6432         },
6433
6434         /**
6435          * Removes the passed window resize listener.
6436          * @param {Function} fn        The method the event invokes
6437          * @param {Object}   scope    The scope of handler
6438          */
6439         removeResizeListener : function(fn, scope){
6440             if(resizeEvent){
6441                 resizeEvent.removeListener(fn, scope);
6442             }
6443         },
6444
6445         // private
6446         fireResize : function(){
6447             if(resizeEvent){
6448                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6449             }   
6450         },
6451         /**
6452          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6453          */
6454         ieDeferSrc : false,
6455         /**
6456          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6457          */
6458         textResizeInterval : 50
6459     };
6460     
6461     /**
6462      * Fix for doc tools
6463      * @scopeAlias pub=Roo.EventManager
6464      */
6465     
6466      /**
6467      * Appends an event handler to an element (shorthand for addListener)
6468      * @param {String/HTMLElement}   element        The html element or id to assign the
6469      * @param {String}   eventName The type of event to listen for
6470      * @param {Function} handler The method the event invokes
6471      * @param {Object}   scope (optional) The scope in which to execute the handler
6472      * function. The handler function's "this" context.
6473      * @param {Object}   options (optional) An object containing handler configuration
6474      * properties. This may contain any of the following properties:<ul>
6475      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6476      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6477      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6478      * <li>preventDefault {Boolean} True to prevent the default action</li>
6479      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6480      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6481      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6482      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6483      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6484      * by the specified number of milliseconds. If the event fires again within that time, the original
6485      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6486      * </ul><br>
6487      * <p>
6488      * <b>Combining Options</b><br>
6489      * Using the options argument, it is possible to combine different types of listeners:<br>
6490      * <br>
6491      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6492      * Code:<pre><code>
6493 el.on('click', this.onClick, this, {
6494     single: true,
6495     delay: 100,
6496     stopEvent : true,
6497     forumId: 4
6498 });</code></pre>
6499      * <p>
6500      * <b>Attaching multiple handlers in 1 call</b><br>
6501       * The method also allows for a single argument to be passed which is a config object containing properties
6502      * which specify multiple handlers.
6503      * <p>
6504      * Code:<pre><code>
6505 el.on({
6506     'click' : {
6507         fn: this.onClick
6508         scope: this,
6509         delay: 100
6510     },
6511     'mouseover' : {
6512         fn: this.onMouseOver
6513         scope: this
6514     },
6515     'mouseout' : {
6516         fn: this.onMouseOut
6517         scope: this
6518     }
6519 });</code></pre>
6520      * <p>
6521      * Or a shorthand syntax:<br>
6522      * Code:<pre><code>
6523 el.on({
6524     'click' : this.onClick,
6525     'mouseover' : this.onMouseOver,
6526     'mouseout' : this.onMouseOut
6527     scope: this
6528 });</code></pre>
6529      */
6530     pub.on = pub.addListener;
6531     pub.un = pub.removeListener;
6532
6533     pub.stoppedMouseDownEvent = new Roo.util.Event();
6534     return pub;
6535 }();
6536 /**
6537   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6538   * @param {Function} fn        The method the event invokes
6539   * @param {Object}   scope    An  object that becomes the scope of the handler
6540   * @param {boolean}  override If true, the obj passed in becomes
6541   *                             the execution scope of the listener
6542   * @member Roo
6543   * @method onReady
6544  */
6545 Roo.onReady = Roo.EventManager.onDocumentReady;
6546
6547 Roo.onReady(function(){
6548     var bd = Roo.get(document.body);
6549     if(!bd){ return; }
6550
6551     var cls = [
6552             Roo.isIE ? "roo-ie"
6553             : Roo.isGecko ? "roo-gecko"
6554             : Roo.isOpera ? "roo-opera"
6555             : Roo.isSafari ? "roo-safari" : ""];
6556
6557     if(Roo.isMac){
6558         cls.push("roo-mac");
6559     }
6560     if(Roo.isLinux){
6561         cls.push("roo-linux");
6562     }
6563     if(Roo.isBorderBox){
6564         cls.push('roo-border-box');
6565     }
6566     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6567         var p = bd.dom.parentNode;
6568         if(p){
6569             p.className += ' roo-strict';
6570         }
6571     }
6572     bd.addClass(cls.join(' '));
6573 });
6574
6575 /**
6576  * @class Roo.EventObject
6577  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6578  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6579  * Example:
6580  * <pre><code>
6581  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6582     e.preventDefault();
6583     var target = e.getTarget();
6584     ...
6585  }
6586  var myDiv = Roo.get("myDiv");
6587  myDiv.on("click", handleClick);
6588  //or
6589  Roo.EventManager.on("myDiv", 'click', handleClick);
6590  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6591  </code></pre>
6592  * @singleton
6593  */
6594 Roo.EventObject = function(){
6595     
6596     var E = Roo.lib.Event;
6597     
6598     // safari keypress events for special keys return bad keycodes
6599     var safariKeys = {
6600         63234 : 37, // left
6601         63235 : 39, // right
6602         63232 : 38, // up
6603         63233 : 40, // down
6604         63276 : 33, // page up
6605         63277 : 34, // page down
6606         63272 : 46, // delete
6607         63273 : 36, // home
6608         63275 : 35  // end
6609     };
6610
6611     // normalize button clicks
6612     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6613                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6614
6615     Roo.EventObjectImpl = function(e){
6616         if(e){
6617             this.setEvent(e.browserEvent || e);
6618         }
6619     };
6620     Roo.EventObjectImpl.prototype = {
6621         /**
6622          * Used to fix doc tools.
6623          * @scope Roo.EventObject.prototype
6624          */
6625             
6626
6627         
6628         
6629         /** The normal browser event */
6630         browserEvent : null,
6631         /** The button pressed in a mouse event */
6632         button : -1,
6633         /** True if the shift key was down during the event */
6634         shiftKey : false,
6635         /** True if the control key was down during the event */
6636         ctrlKey : false,
6637         /** True if the alt key was down during the event */
6638         altKey : false,
6639
6640         /** Key constant 
6641         * @type Number */
6642         BACKSPACE : 8,
6643         /** Key constant 
6644         * @type Number */
6645         TAB : 9,
6646         /** Key constant 
6647         * @type Number */
6648         RETURN : 13,
6649         /** Key constant 
6650         * @type Number */
6651         ENTER : 13,
6652         /** Key constant 
6653         * @type Number */
6654         SHIFT : 16,
6655         /** Key constant 
6656         * @type Number */
6657         CONTROL : 17,
6658         /** Key constant 
6659         * @type Number */
6660         ESC : 27,
6661         /** Key constant 
6662         * @type Number */
6663         SPACE : 32,
6664         /** Key constant 
6665         * @type Number */
6666         PAGEUP : 33,
6667         /** Key constant 
6668         * @type Number */
6669         PAGEDOWN : 34,
6670         /** Key constant 
6671         * @type Number */
6672         END : 35,
6673         /** Key constant 
6674         * @type Number */
6675         HOME : 36,
6676         /** Key constant 
6677         * @type Number */
6678         LEFT : 37,
6679         /** Key constant 
6680         * @type Number */
6681         UP : 38,
6682         /** Key constant 
6683         * @type Number */
6684         RIGHT : 39,
6685         /** Key constant 
6686         * @type Number */
6687         DOWN : 40,
6688         /** Key constant 
6689         * @type Number */
6690         DELETE : 46,
6691         /** Key constant 
6692         * @type Number */
6693         F5 : 116,
6694
6695            /** @private */
6696         setEvent : function(e){
6697             if(e == this || (e && e.browserEvent)){ // already wrapped
6698                 return e;
6699             }
6700             this.browserEvent = e;
6701             if(e){
6702                 // normalize buttons
6703                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6704                 if(e.type == 'click' && this.button == -1){
6705                     this.button = 0;
6706                 }
6707                 this.type = e.type;
6708                 this.shiftKey = e.shiftKey;
6709                 // mac metaKey behaves like ctrlKey
6710                 this.ctrlKey = e.ctrlKey || e.metaKey;
6711                 this.altKey = e.altKey;
6712                 // in getKey these will be normalized for the mac
6713                 this.keyCode = e.keyCode;
6714                 // keyup warnings on firefox.
6715                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6716                 // cache the target for the delayed and or buffered events
6717                 this.target = E.getTarget(e);
6718                 // same for XY
6719                 this.xy = E.getXY(e);
6720             }else{
6721                 this.button = -1;
6722                 this.shiftKey = false;
6723                 this.ctrlKey = false;
6724                 this.altKey = false;
6725                 this.keyCode = 0;
6726                 this.charCode =0;
6727                 this.target = null;
6728                 this.xy = [0, 0];
6729             }
6730             return this;
6731         },
6732
6733         /**
6734          * Stop the event (preventDefault and stopPropagation)
6735          */
6736         stopEvent : function(){
6737             if(this.browserEvent){
6738                 if(this.browserEvent.type == 'mousedown'){
6739                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6740                 }
6741                 E.stopEvent(this.browserEvent);
6742             }
6743         },
6744
6745         /**
6746          * Prevents the browsers default handling of the event.
6747          */
6748         preventDefault : function(){
6749             if(this.browserEvent){
6750                 E.preventDefault(this.browserEvent);
6751             }
6752         },
6753
6754         /** @private */
6755         isNavKeyPress : function(){
6756             var k = this.keyCode;
6757             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6758             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6759         },
6760
6761         isSpecialKey : function(){
6762             var k = this.keyCode;
6763             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6764             (k == 16) || (k == 17) ||
6765             (k >= 18 && k <= 20) ||
6766             (k >= 33 && k <= 35) ||
6767             (k >= 36 && k <= 39) ||
6768             (k >= 44 && k <= 45);
6769         },
6770         /**
6771          * Cancels bubbling of the event.
6772          */
6773         stopPropagation : function(){
6774             if(this.browserEvent){
6775                 if(this.type == 'mousedown'){
6776                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6777                 }
6778                 E.stopPropagation(this.browserEvent);
6779             }
6780         },
6781
6782         /**
6783          * Gets the key code for the event.
6784          * @return {Number}
6785          */
6786         getCharCode : function(){
6787             return this.charCode || this.keyCode;
6788         },
6789
6790         /**
6791          * Returns a normalized keyCode for the event.
6792          * @return {Number} The key code
6793          */
6794         getKey : function(){
6795             var k = this.keyCode || this.charCode;
6796             return Roo.isSafari ? (safariKeys[k] || k) : k;
6797         },
6798
6799         /**
6800          * Gets the x coordinate of the event.
6801          * @return {Number}
6802          */
6803         getPageX : function(){
6804             return this.xy[0];
6805         },
6806
6807         /**
6808          * Gets the y coordinate of the event.
6809          * @return {Number}
6810          */
6811         getPageY : function(){
6812             return this.xy[1];
6813         },
6814
6815         /**
6816          * Gets the time of the event.
6817          * @return {Number}
6818          */
6819         getTime : function(){
6820             if(this.browserEvent){
6821                 return E.getTime(this.browserEvent);
6822             }
6823             return null;
6824         },
6825
6826         /**
6827          * Gets the page coordinates of the event.
6828          * @return {Array} The xy values like [x, y]
6829          */
6830         getXY : function(){
6831             return this.xy;
6832         },
6833
6834         /**
6835          * Gets the target for the event.
6836          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6837          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6838                 search as a number or element (defaults to 10 || document.body)
6839          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6840          * @return {HTMLelement}
6841          */
6842         getTarget : function(selector, maxDepth, returnEl){
6843             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6844         },
6845         /**
6846          * Gets the related target.
6847          * @return {HTMLElement}
6848          */
6849         getRelatedTarget : function(){
6850             if(this.browserEvent){
6851                 return E.getRelatedTarget(this.browserEvent);
6852             }
6853             return null;
6854         },
6855
6856         /**
6857          * Normalizes mouse wheel delta across browsers
6858          * @return {Number} The delta
6859          */
6860         getWheelDelta : function(){
6861             var e = this.browserEvent;
6862             var delta = 0;
6863             if(e.wheelDelta){ /* IE/Opera. */
6864                 delta = e.wheelDelta/120;
6865             }else if(e.detail){ /* Mozilla case. */
6866                 delta = -e.detail/3;
6867             }
6868             return delta;
6869         },
6870
6871         /**
6872          * Returns true if the control, meta, shift or alt key was pressed during this event.
6873          * @return {Boolean}
6874          */
6875         hasModifier : function(){
6876             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6877         },
6878
6879         /**
6880          * Returns true if the target of this event equals el or is a child of el
6881          * @param {String/HTMLElement/Element} el
6882          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6883          * @return {Boolean}
6884          */
6885         within : function(el, related){
6886             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6887             return t && Roo.fly(el).contains(t);
6888         },
6889
6890         getPoint : function(){
6891             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6892         }
6893     };
6894
6895     return new Roo.EventObjectImpl();
6896 }();
6897             
6898     /*
6899  * Based on:
6900  * Ext JS Library 1.1.1
6901  * Copyright(c) 2006-2007, Ext JS, LLC.
6902  *
6903  * Originally Released Under LGPL - original licence link has changed is not relivant.
6904  *
6905  * Fork - LGPL
6906  * <script type="text/javascript">
6907  */
6908
6909  
6910 // was in Composite Element!??!?!
6911  
6912 (function(){
6913     var D = Roo.lib.Dom;
6914     var E = Roo.lib.Event;
6915     var A = Roo.lib.Anim;
6916
6917     // local style camelizing for speed
6918     var propCache = {};
6919     var camelRe = /(-[a-z])/gi;
6920     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6921     var view = document.defaultView;
6922
6923 /**
6924  * @class Roo.Element
6925  * Represents an Element in the DOM.<br><br>
6926  * Usage:<br>
6927 <pre><code>
6928 var el = Roo.get("my-div");
6929
6930 // or with getEl
6931 var el = getEl("my-div");
6932
6933 // or with a DOM element
6934 var el = Roo.get(myDivElement);
6935 </code></pre>
6936  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6937  * each call instead of constructing a new one.<br><br>
6938  * <b>Animations</b><br />
6939  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6940  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6941 <pre>
6942 Option    Default   Description
6943 --------- --------  ---------------------------------------------
6944 duration  .35       The duration of the animation in seconds
6945 easing    easeOut   The YUI easing method
6946 callback  none      A function to execute when the anim completes
6947 scope     this      The scope (this) of the callback function
6948 </pre>
6949 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6950 * manipulate the animation. Here's an example:
6951 <pre><code>
6952 var el = Roo.get("my-div");
6953
6954 // no animation
6955 el.setWidth(100);
6956
6957 // default animation
6958 el.setWidth(100, true);
6959
6960 // animation with some options set
6961 el.setWidth(100, {
6962     duration: 1,
6963     callback: this.foo,
6964     scope: this
6965 });
6966
6967 // using the "anim" property to get the Anim object
6968 var opt = {
6969     duration: 1,
6970     callback: this.foo,
6971     scope: this
6972 };
6973 el.setWidth(100, opt);
6974 ...
6975 if(opt.anim.isAnimated()){
6976     opt.anim.stop();
6977 }
6978 </code></pre>
6979 * <b> Composite (Collections of) Elements</b><br />
6980  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6981  * @constructor Create a new Element directly.
6982  * @param {String/HTMLElement} element
6983  * @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).
6984  */
6985     Roo.Element = function(element, forceNew){
6986         var dom = typeof element == "string" ?
6987                 document.getElementById(element) : element;
6988         if(!dom){ // invalid id/element
6989             return null;
6990         }
6991         var id = dom.id;
6992         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6993             return Roo.Element.cache[id];
6994         }
6995
6996         /**
6997          * The DOM element
6998          * @type HTMLElement
6999          */
7000         this.dom = dom;
7001
7002         /**
7003          * The DOM element ID
7004          * @type String
7005          */
7006         this.id = id || Roo.id(dom);
7007     };
7008
7009     var El = Roo.Element;
7010
7011     El.prototype = {
7012         /**
7013          * The element's default display mode  (defaults to "")
7014          * @type String
7015          */
7016         originalDisplay : "",
7017
7018         visibilityMode : 1,
7019         /**
7020          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7021          * @type String
7022          */
7023         defaultUnit : "px",
7024         /**
7025          * Sets the element's visibility mode. When setVisible() is called it
7026          * will use this to determine whether to set the visibility or the display property.
7027          * @param visMode Element.VISIBILITY or Element.DISPLAY
7028          * @return {Roo.Element} this
7029          */
7030         setVisibilityMode : function(visMode){
7031             this.visibilityMode = visMode;
7032             return this;
7033         },
7034         /**
7035          * Convenience method for setVisibilityMode(Element.DISPLAY)
7036          * @param {String} display (optional) What to set display to when visible
7037          * @return {Roo.Element} this
7038          */
7039         enableDisplayMode : function(display){
7040             this.setVisibilityMode(El.DISPLAY);
7041             if(typeof display != "undefined") this.originalDisplay = display;
7042             return this;
7043         },
7044
7045         /**
7046          * 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)
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          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7051          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7052          */
7053         findParent : function(simpleSelector, maxDepth, returnEl){
7054             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7055             maxDepth = maxDepth || 50;
7056             if(typeof maxDepth != "number"){
7057                 stopEl = Roo.getDom(maxDepth);
7058                 maxDepth = 10;
7059             }
7060             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7061                 if(dq.is(p, simpleSelector)){
7062                     return returnEl ? Roo.get(p) : p;
7063                 }
7064                 depth++;
7065                 p = p.parentNode;
7066             }
7067             return null;
7068         },
7069
7070
7071         /**
7072          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7073          * @param {String} selector The simple selector to test
7074          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7075                 search as a number or element (defaults to 10 || document.body)
7076          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7077          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7078          */
7079         findParentNode : function(simpleSelector, maxDepth, returnEl){
7080             var p = Roo.fly(this.dom.parentNode, '_internal');
7081             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7082         },
7083
7084         /**
7085          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7086          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7087          * @param {String} selector The simple selector to test
7088          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7089                 search as a number or element (defaults to 10 || document.body)
7090          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7091          */
7092         up : function(simpleSelector, maxDepth){
7093             return this.findParentNode(simpleSelector, maxDepth, true);
7094         },
7095
7096
7097
7098         /**
7099          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7100          * @param {String} selector The simple selector to test
7101          * @return {Boolean} True if this element matches the selector, else false
7102          */
7103         is : function(simpleSelector){
7104             return Roo.DomQuery.is(this.dom, simpleSelector);
7105         },
7106
7107         /**
7108          * Perform animation on this element.
7109          * @param {Object} args The YUI animation control args
7110          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7111          * @param {Function} onComplete (optional) Function to call when animation completes
7112          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7113          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7114          * @return {Roo.Element} this
7115          */
7116         animate : function(args, duration, onComplete, easing, animType){
7117             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7118             return this;
7119         },
7120
7121         /*
7122          * @private Internal animation call
7123          */
7124         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7125             animType = animType || 'run';
7126             opt = opt || {};
7127             var anim = Roo.lib.Anim[animType](
7128                 this.dom, args,
7129                 (opt.duration || defaultDur) || .35,
7130                 (opt.easing || defaultEase) || 'easeOut',
7131                 function(){
7132                     Roo.callback(cb, this);
7133                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7134                 },
7135                 this
7136             );
7137             opt.anim = anim;
7138             return anim;
7139         },
7140
7141         // private legacy anim prep
7142         preanim : function(a, i){
7143             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7144         },
7145
7146         /**
7147          * Removes worthless text nodes
7148          * @param {Boolean} forceReclean (optional) By default the element
7149          * keeps track if it has been cleaned already so
7150          * you can call this over and over. However, if you update the element and
7151          * need to force a reclean, you can pass true.
7152          */
7153         clean : function(forceReclean){
7154             if(this.isCleaned && forceReclean !== true){
7155                 return this;
7156             }
7157             var ns = /\S/;
7158             var d = this.dom, n = d.firstChild, ni = -1;
7159             while(n){
7160                 var nx = n.nextSibling;
7161                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7162                     d.removeChild(n);
7163                 }else{
7164                     n.nodeIndex = ++ni;
7165                 }
7166                 n = nx;
7167             }
7168             this.isCleaned = true;
7169             return this;
7170         },
7171
7172         // private
7173         calcOffsetsTo : function(el){
7174             el = Roo.get(el);
7175             var d = el.dom;
7176             var restorePos = false;
7177             if(el.getStyle('position') == 'static'){
7178                 el.position('relative');
7179                 restorePos = true;
7180             }
7181             var x = 0, y =0;
7182             var op = this.dom;
7183             while(op && op != d && op.tagName != 'HTML'){
7184                 x+= op.offsetLeft;
7185                 y+= op.offsetTop;
7186                 op = op.offsetParent;
7187             }
7188             if(restorePos){
7189                 el.position('static');
7190             }
7191             return [x, y];
7192         },
7193
7194         /**
7195          * Scrolls this element into view within the passed container.
7196          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7197          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7198          * @return {Roo.Element} this
7199          */
7200         scrollIntoView : function(container, hscroll){
7201             var c = Roo.getDom(container) || document.body;
7202             var el = this.dom;
7203
7204             var o = this.calcOffsetsTo(c),
7205                 l = o[0],
7206                 t = o[1],
7207                 b = t+el.offsetHeight,
7208                 r = l+el.offsetWidth;
7209
7210             var ch = c.clientHeight;
7211             var ct = parseInt(c.scrollTop, 10);
7212             var cl = parseInt(c.scrollLeft, 10);
7213             var cb = ct + ch;
7214             var cr = cl + c.clientWidth;
7215
7216             if(t < ct){
7217                 c.scrollTop = t;
7218             }else if(b > cb){
7219                 c.scrollTop = b-ch;
7220             }
7221
7222             if(hscroll !== false){
7223                 if(l < cl){
7224                     c.scrollLeft = l;
7225                 }else if(r > cr){
7226                     c.scrollLeft = r-c.clientWidth;
7227                 }
7228             }
7229             return this;
7230         },
7231
7232         // private
7233         scrollChildIntoView : function(child, hscroll){
7234             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7235         },
7236
7237         /**
7238          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7239          * the new height may not be available immediately.
7240          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7241          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7242          * @param {Function} onComplete (optional) Function to call when animation completes
7243          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7244          * @return {Roo.Element} this
7245          */
7246         autoHeight : function(animate, duration, onComplete, easing){
7247             var oldHeight = this.getHeight();
7248             this.clip();
7249             this.setHeight(1); // force clipping
7250             setTimeout(function(){
7251                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7252                 if(!animate){
7253                     this.setHeight(height);
7254                     this.unclip();
7255                     if(typeof onComplete == "function"){
7256                         onComplete();
7257                     }
7258                 }else{
7259                     this.setHeight(oldHeight); // restore original height
7260                     this.setHeight(height, animate, duration, function(){
7261                         this.unclip();
7262                         if(typeof onComplete == "function") onComplete();
7263                     }.createDelegate(this), easing);
7264                 }
7265             }.createDelegate(this), 0);
7266             return this;
7267         },
7268
7269         /**
7270          * Returns true if this element is an ancestor of the passed element
7271          * @param {HTMLElement/String} el The element to check
7272          * @return {Boolean} True if this element is an ancestor of el, else false
7273          */
7274         contains : function(el){
7275             if(!el){return false;}
7276             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7277         },
7278
7279         /**
7280          * Checks whether the element is currently visible using both visibility and display properties.
7281          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7282          * @return {Boolean} True if the element is currently visible, else false
7283          */
7284         isVisible : function(deep) {
7285             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7286             if(deep !== true || !vis){
7287                 return vis;
7288             }
7289             var p = this.dom.parentNode;
7290             while(p && p.tagName.toLowerCase() != "body"){
7291                 if(!Roo.fly(p, '_isVisible').isVisible()){
7292                     return false;
7293                 }
7294                 p = p.parentNode;
7295             }
7296             return true;
7297         },
7298
7299         /**
7300          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7301          * @param {String} selector The CSS selector
7302          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7303          * @return {CompositeElement/CompositeElementLite} The composite element
7304          */
7305         select : function(selector, unique){
7306             return El.select(selector, unique, this.dom);
7307         },
7308
7309         /**
7310          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7311          * @param {String} selector The CSS selector
7312          * @return {Array} An array of the matched nodes
7313          */
7314         query : function(selector, unique){
7315             return Roo.DomQuery.select(selector, this.dom);
7316         },
7317
7318         /**
7319          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7320          * @param {String} selector The CSS selector
7321          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7322          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7323          */
7324         child : function(selector, returnDom){
7325             var n = Roo.DomQuery.selectNode(selector, this.dom);
7326             return returnDom ? n : Roo.get(n);
7327         },
7328
7329         /**
7330          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7331          * @param {String} selector The CSS selector
7332          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7333          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7334          */
7335         down : function(selector, returnDom){
7336             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7337             return returnDom ? n : Roo.get(n);
7338         },
7339
7340         /**
7341          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7342          * @param {String} group The group the DD object is member of
7343          * @param {Object} config The DD config object
7344          * @param {Object} overrides An object containing methods to override/implement on the DD object
7345          * @return {Roo.dd.DD} The DD object
7346          */
7347         initDD : function(group, config, overrides){
7348             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7349             return Roo.apply(dd, overrides);
7350         },
7351
7352         /**
7353          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7354          * @param {String} group The group the DDProxy object is member of
7355          * @param {Object} config The DDProxy config object
7356          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7357          * @return {Roo.dd.DDProxy} The DDProxy object
7358          */
7359         initDDProxy : function(group, config, overrides){
7360             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7361             return Roo.apply(dd, overrides);
7362         },
7363
7364         /**
7365          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7366          * @param {String} group The group the DDTarget object is member of
7367          * @param {Object} config The DDTarget config object
7368          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7369          * @return {Roo.dd.DDTarget} The DDTarget object
7370          */
7371         initDDTarget : function(group, config, overrides){
7372             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7373             return Roo.apply(dd, overrides);
7374         },
7375
7376         /**
7377          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7378          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7379          * @param {Boolean} visible Whether the element is visible
7380          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7381          * @return {Roo.Element} this
7382          */
7383          setVisible : function(visible, animate){
7384             if(!animate || !A){
7385                 if(this.visibilityMode == El.DISPLAY){
7386                     this.setDisplayed(visible);
7387                 }else{
7388                     this.fixDisplay();
7389                     this.dom.style.visibility = visible ? "visible" : "hidden";
7390                 }
7391             }else{
7392                 // closure for composites
7393                 var dom = this.dom;
7394                 var visMode = this.visibilityMode;
7395                 if(visible){
7396                     this.setOpacity(.01);
7397                     this.setVisible(true);
7398                 }
7399                 this.anim({opacity: { to: (visible?1:0) }},
7400                       this.preanim(arguments, 1),
7401                       null, .35, 'easeIn', function(){
7402                          if(!visible){
7403                              if(visMode == El.DISPLAY){
7404                                  dom.style.display = "none";
7405                              }else{
7406                                  dom.style.visibility = "hidden";
7407                              }
7408                              Roo.get(dom).setOpacity(1);
7409                          }
7410                      });
7411             }
7412             return this;
7413         },
7414
7415         /**
7416          * Returns true if display is not "none"
7417          * @return {Boolean}
7418          */
7419         isDisplayed : function() {
7420             return this.getStyle("display") != "none";
7421         },
7422
7423         /**
7424          * Toggles the element's visibility or display, depending on visibility mode.
7425          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7426          * @return {Roo.Element} this
7427          */
7428         toggle : function(animate){
7429             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7430             return this;
7431         },
7432
7433         /**
7434          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7435          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7436          * @return {Roo.Element} this
7437          */
7438         setDisplayed : function(value) {
7439             if(typeof value == "boolean"){
7440                value = value ? this.originalDisplay : "none";
7441             }
7442             this.setStyle("display", value);
7443             return this;
7444         },
7445
7446         /**
7447          * Tries to focus the element. Any exceptions are caught and ignored.
7448          * @return {Roo.Element} this
7449          */
7450         focus : function() {
7451             try{
7452                 this.dom.focus();
7453             }catch(e){}
7454             return this;
7455         },
7456
7457         /**
7458          * Tries to blur the element. Any exceptions are caught and ignored.
7459          * @return {Roo.Element} this
7460          */
7461         blur : function() {
7462             try{
7463                 this.dom.blur();
7464             }catch(e){}
7465             return this;
7466         },
7467
7468         /**
7469          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7470          * @param {String/Array} className The CSS class to add, or an array of classes
7471          * @return {Roo.Element} this
7472          */
7473         addClass : function(className){
7474             if(className instanceof Array){
7475                 for(var i = 0, len = className.length; i < len; i++) {
7476                     this.addClass(className[i]);
7477                 }
7478             }else{
7479                 if(className && !this.hasClass(className)){
7480                     this.dom.className = this.dom.className + " " + className;
7481                 }
7482             }
7483             return this;
7484         },
7485
7486         /**
7487          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7488          * @param {String/Array} className The CSS class to add, or an array of classes
7489          * @return {Roo.Element} this
7490          */
7491         radioClass : function(className){
7492             var siblings = this.dom.parentNode.childNodes;
7493             for(var i = 0; i < siblings.length; i++) {
7494                 var s = siblings[i];
7495                 if(s.nodeType == 1){
7496                     Roo.get(s).removeClass(className);
7497                 }
7498             }
7499             this.addClass(className);
7500             return this;
7501         },
7502
7503         /**
7504          * Removes one or more CSS classes from the element.
7505          * @param {String/Array} className The CSS class to remove, or an array of classes
7506          * @return {Roo.Element} this
7507          */
7508         removeClass : function(className){
7509             if(!className || !this.dom.className){
7510                 return this;
7511             }
7512             if(className instanceof Array){
7513                 for(var i = 0, len = className.length; i < len; i++) {
7514                     this.removeClass(className[i]);
7515                 }
7516             }else{
7517                 if(this.hasClass(className)){
7518                     var re = this.classReCache[className];
7519                     if (!re) {
7520                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7521                        this.classReCache[className] = re;
7522                     }
7523                     this.dom.className =
7524                         this.dom.className.replace(re, " ");
7525                 }
7526             }
7527             return this;
7528         },
7529
7530         // private
7531         classReCache: {},
7532
7533         /**
7534          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7535          * @param {String} className The CSS class to toggle
7536          * @return {Roo.Element} this
7537          */
7538         toggleClass : function(className){
7539             if(this.hasClass(className)){
7540                 this.removeClass(className);
7541             }else{
7542                 this.addClass(className);
7543             }
7544             return this;
7545         },
7546
7547         /**
7548          * Checks if the specified CSS class exists on this element's DOM node.
7549          * @param {String} className The CSS class to check for
7550          * @return {Boolean} True if the class exists, else false
7551          */
7552         hasClass : function(className){
7553             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7554         },
7555
7556         /**
7557          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7558          * @param {String} oldClassName The CSS class to replace
7559          * @param {String} newClassName The replacement CSS class
7560          * @return {Roo.Element} this
7561          */
7562         replaceClass : function(oldClassName, newClassName){
7563             this.removeClass(oldClassName);
7564             this.addClass(newClassName);
7565             return this;
7566         },
7567
7568         /**
7569          * Returns an object with properties matching the styles requested.
7570          * For example, el.getStyles('color', 'font-size', 'width') might return
7571          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7572          * @param {String} style1 A style name
7573          * @param {String} style2 A style name
7574          * @param {String} etc.
7575          * @return {Object} The style object
7576          */
7577         getStyles : function(){
7578             var a = arguments, len = a.length, r = {};
7579             for(var i = 0; i < len; i++){
7580                 r[a[i]] = this.getStyle(a[i]);
7581             }
7582             return r;
7583         },
7584
7585         /**
7586          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7587          * @param {String} property The style property whose value is returned.
7588          * @return {String} The current value of the style property for this element.
7589          */
7590         getStyle : function(){
7591             return view && view.getComputedStyle ?
7592                 function(prop){
7593                     var el = this.dom, v, cs, camel;
7594                     if(prop == 'float'){
7595                         prop = "cssFloat";
7596                     }
7597                     if(el.style && (v = el.style[prop])){
7598                         return v;
7599                     }
7600                     if(cs = view.getComputedStyle(el, "")){
7601                         if(!(camel = propCache[prop])){
7602                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7603                         }
7604                         return cs[camel];
7605                     }
7606                     return null;
7607                 } :
7608                 function(prop){
7609                     var el = this.dom, v, cs, camel;
7610                     if(prop == 'opacity'){
7611                         if(typeof el.style.filter == 'string'){
7612                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7613                             if(m){
7614                                 var fv = parseFloat(m[1]);
7615                                 if(!isNaN(fv)){
7616                                     return fv ? fv / 100 : 0;
7617                                 }
7618                             }
7619                         }
7620                         return 1;
7621                     }else if(prop == 'float'){
7622                         prop = "styleFloat";
7623                     }
7624                     if(!(camel = propCache[prop])){
7625                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7626                     }
7627                     if(v = el.style[camel]){
7628                         return v;
7629                     }
7630                     if(cs = el.currentStyle){
7631                         return cs[camel];
7632                     }
7633                     return null;
7634                 };
7635         }(),
7636
7637         /**
7638          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7639          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7640          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7641          * @return {Roo.Element} this
7642          */
7643         setStyle : function(prop, value){
7644             if(typeof prop == "string"){
7645                 
7646                 if (prop == 'float') {
7647                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7648                     return this;
7649                 }
7650                 
7651                 var camel;
7652                 if(!(camel = propCache[prop])){
7653                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7654                 }
7655                 
7656                 if(camel == 'opacity') {
7657                     this.setOpacity(value);
7658                 }else{
7659                     this.dom.style[camel] = value;
7660                 }
7661             }else{
7662                 for(var style in prop){
7663                     if(typeof prop[style] != "function"){
7664                        this.setStyle(style, prop[style]);
7665                     }
7666                 }
7667             }
7668             return this;
7669         },
7670
7671         /**
7672          * More flexible version of {@link #setStyle} for setting style properties.
7673          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7674          * a function which returns such a specification.
7675          * @return {Roo.Element} this
7676          */
7677         applyStyles : function(style){
7678             Roo.DomHelper.applyStyles(this.dom, style);
7679             return this;
7680         },
7681
7682         /**
7683           * 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).
7684           * @return {Number} The X position of the element
7685           */
7686         getX : function(){
7687             return D.getX(this.dom);
7688         },
7689
7690         /**
7691           * 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).
7692           * @return {Number} The Y position of the element
7693           */
7694         getY : function(){
7695             return D.getY(this.dom);
7696         },
7697
7698         /**
7699           * 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).
7700           * @return {Array} The XY position of the element
7701           */
7702         getXY : function(){
7703             return D.getXY(this.dom);
7704         },
7705
7706         /**
7707          * 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).
7708          * @param {Number} The X position of the element
7709          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7710          * @return {Roo.Element} this
7711          */
7712         setX : function(x, animate){
7713             if(!animate || !A){
7714                 D.setX(this.dom, x);
7715             }else{
7716                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7717             }
7718             return this;
7719         },
7720
7721         /**
7722          * 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).
7723          * @param {Number} The Y position of the element
7724          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7725          * @return {Roo.Element} this
7726          */
7727         setY : function(y, animate){
7728             if(!animate || !A){
7729                 D.setY(this.dom, y);
7730             }else{
7731                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7732             }
7733             return this;
7734         },
7735
7736         /**
7737          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7738          * @param {String} left The left CSS property value
7739          * @return {Roo.Element} this
7740          */
7741         setLeft : function(left){
7742             this.setStyle("left", this.addUnits(left));
7743             return this;
7744         },
7745
7746         /**
7747          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7748          * @param {String} top The top CSS property value
7749          * @return {Roo.Element} this
7750          */
7751         setTop : function(top){
7752             this.setStyle("top", this.addUnits(top));
7753             return this;
7754         },
7755
7756         /**
7757          * Sets the element's CSS right style.
7758          * @param {String} right The right CSS property value
7759          * @return {Roo.Element} this
7760          */
7761         setRight : function(right){
7762             this.setStyle("right", this.addUnits(right));
7763             return this;
7764         },
7765
7766         /**
7767          * Sets the element's CSS bottom style.
7768          * @param {String} bottom The bottom CSS property value
7769          * @return {Roo.Element} this
7770          */
7771         setBottom : function(bottom){
7772             this.setStyle("bottom", this.addUnits(bottom));
7773             return this;
7774         },
7775
7776         /**
7777          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7778          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7779          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7780          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7781          * @return {Roo.Element} this
7782          */
7783         setXY : function(pos, animate){
7784             if(!animate || !A){
7785                 D.setXY(this.dom, pos);
7786             }else{
7787                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7788             }
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7794          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7795          * @param {Number} x X value for new position (coordinates are page-based)
7796          * @param {Number} y Y value for new position (coordinates are page-based)
7797          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7798          * @return {Roo.Element} this
7799          */
7800         setLocation : function(x, y, animate){
7801             this.setXY([x, y], this.preanim(arguments, 2));
7802             return this;
7803         },
7804
7805         /**
7806          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7807          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7808          * @param {Number} x X value for new position (coordinates are page-based)
7809          * @param {Number} y Y value for new position (coordinates are page-based)
7810          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7811          * @return {Roo.Element} this
7812          */
7813         moveTo : function(x, y, animate){
7814             this.setXY([x, y], this.preanim(arguments, 2));
7815             return this;
7816         },
7817
7818         /**
7819          * Returns the region of the given element.
7820          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7821          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7822          */
7823         getRegion : function(){
7824             return D.getRegion(this.dom);
7825         },
7826
7827         /**
7828          * Returns the offset height of the element
7829          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7830          * @return {Number} The element's height
7831          */
7832         getHeight : function(contentHeight){
7833             var h = this.dom.offsetHeight || 0;
7834             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7835         },
7836
7837         /**
7838          * Returns the offset width of the element
7839          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7840          * @return {Number} The element's width
7841          */
7842         getWidth : function(contentWidth){
7843             var w = this.dom.offsetWidth || 0;
7844             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7845         },
7846
7847         /**
7848          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7849          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7850          * if a height has not been set using CSS.
7851          * @return {Number}
7852          */
7853         getComputedHeight : function(){
7854             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7855             if(!h){
7856                 h = parseInt(this.getStyle('height'), 10) || 0;
7857                 if(!this.isBorderBox()){
7858                     h += this.getFrameWidth('tb');
7859                 }
7860             }
7861             return h;
7862         },
7863
7864         /**
7865          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7866          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7867          * if a width has not been set using CSS.
7868          * @return {Number}
7869          */
7870         getComputedWidth : function(){
7871             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7872             if(!w){
7873                 w = parseInt(this.getStyle('width'), 10) || 0;
7874                 if(!this.isBorderBox()){
7875                     w += this.getFrameWidth('lr');
7876                 }
7877             }
7878             return w;
7879         },
7880
7881         /**
7882          * Returns the size of the element.
7883          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7884          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7885          */
7886         getSize : function(contentSize){
7887             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7888         },
7889
7890         /**
7891          * Returns the width and height of the viewport.
7892          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7893          */
7894         getViewSize : function(){
7895             var d = this.dom, doc = document, aw = 0, ah = 0;
7896             if(d == doc || d == doc.body){
7897                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7898             }else{
7899                 return {
7900                     width : d.clientWidth,
7901                     height: d.clientHeight
7902                 };
7903             }
7904         },
7905
7906         /**
7907          * Returns the value of the "value" attribute
7908          * @param {Boolean} asNumber true to parse the value as a number
7909          * @return {String/Number}
7910          */
7911         getValue : function(asNumber){
7912             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7913         },
7914
7915         // private
7916         adjustWidth : function(width){
7917             if(typeof width == "number"){
7918                 if(this.autoBoxAdjust && !this.isBorderBox()){
7919                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7920                 }
7921                 if(width < 0){
7922                     width = 0;
7923                 }
7924             }
7925             return width;
7926         },
7927
7928         // private
7929         adjustHeight : function(height){
7930             if(typeof height == "number"){
7931                if(this.autoBoxAdjust && !this.isBorderBox()){
7932                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7933                }
7934                if(height < 0){
7935                    height = 0;
7936                }
7937             }
7938             return height;
7939         },
7940
7941         /**
7942          * Set the width of the element
7943          * @param {Number} width The new width
7944          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7945          * @return {Roo.Element} this
7946          */
7947         setWidth : function(width, animate){
7948             width = this.adjustWidth(width);
7949             if(!animate || !A){
7950                 this.dom.style.width = this.addUnits(width);
7951             }else{
7952                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7953             }
7954             return this;
7955         },
7956
7957         /**
7958          * Set the height of the element
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          setHeight : function(height, animate){
7964             height = this.adjustHeight(height);
7965             if(!animate || !A){
7966                 this.dom.style.height = this.addUnits(height);
7967             }else{
7968                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7975          * @param {Number} width The new width
7976          * @param {Number} height The new height
7977          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7978          * @return {Roo.Element} this
7979          */
7980          setSize : function(width, height, animate){
7981             if(typeof width == "object"){ // in case of object from getSize()
7982                 height = width.height; width = width.width;
7983             }
7984             width = this.adjustWidth(width); height = this.adjustHeight(height);
7985             if(!animate || !A){
7986                 this.dom.style.width = this.addUnits(width);
7987                 this.dom.style.height = this.addUnits(height);
7988             }else{
7989                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7990             }
7991             return this;
7992         },
7993
7994         /**
7995          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7996          * @param {Number} x X value for new position (coordinates are page-based)
7997          * @param {Number} y Y value for new position (coordinates are page-based)
7998          * @param {Number} width The new width
7999          * @param {Number} height The new height
8000          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8001          * @return {Roo.Element} this
8002          */
8003         setBounds : function(x, y, width, height, animate){
8004             if(!animate || !A){
8005                 this.setSize(width, height);
8006                 this.setLocation(x, y);
8007             }else{
8008                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8009                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8010                               this.preanim(arguments, 4), 'motion');
8011             }
8012             return this;
8013         },
8014
8015         /**
8016          * 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.
8017          * @param {Roo.lib.Region} region The region to fill
8018          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8019          * @return {Roo.Element} this
8020          */
8021         setRegion : function(region, animate){
8022             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8023             return this;
8024         },
8025
8026         /**
8027          * Appends an event handler
8028          *
8029          * @param {String}   eventName     The type of event to append
8030          * @param {Function} fn        The method the event invokes
8031          * @param {Object} scope       (optional) The scope (this object) of the fn
8032          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8033          */
8034         addListener : function(eventName, fn, scope, options){
8035             if (this.dom) {
8036                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8037             }
8038         },
8039
8040         /**
8041          * Removes an event handler from this element
8042          * @param {String} eventName the type of event to remove
8043          * @param {Function} fn the method the event invokes
8044          * @return {Roo.Element} this
8045          */
8046         removeListener : function(eventName, fn){
8047             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8048             return this;
8049         },
8050
8051         /**
8052          * Removes all previous added listeners from this element
8053          * @return {Roo.Element} this
8054          */
8055         removeAllListeners : function(){
8056             E.purgeElement(this.dom);
8057             return this;
8058         },
8059
8060         relayEvent : function(eventName, observable){
8061             this.on(eventName, function(e){
8062                 observable.fireEvent(eventName, e);
8063             });
8064         },
8065
8066         /**
8067          * Set the opacity of the element
8068          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8070          * @return {Roo.Element} this
8071          */
8072          setOpacity : function(opacity, animate){
8073             if(!animate || !A){
8074                 var s = this.dom.style;
8075                 if(Roo.isIE){
8076                     s.zoom = 1;
8077                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8078                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8079                 }else{
8080                     s.opacity = opacity;
8081                 }
8082             }else{
8083                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8084             }
8085             return this;
8086         },
8087
8088         /**
8089          * Gets the left X coordinate
8090          * @param {Boolean} local True to get the local css position instead of page coordinate
8091          * @return {Number}
8092          */
8093         getLeft : function(local){
8094             if(!local){
8095                 return this.getX();
8096             }else{
8097                 return parseInt(this.getStyle("left"), 10) || 0;
8098             }
8099         },
8100
8101         /**
8102          * Gets the right X coordinate of the element (element X position + element width)
8103          * @param {Boolean} local True to get the local css position instead of page coordinate
8104          * @return {Number}
8105          */
8106         getRight : function(local){
8107             if(!local){
8108                 return this.getX() + this.getWidth();
8109             }else{
8110                 return (this.getLeft(true) + this.getWidth()) || 0;
8111             }
8112         },
8113
8114         /**
8115          * Gets the top Y coordinate
8116          * @param {Boolean} local True to get the local css position instead of page coordinate
8117          * @return {Number}
8118          */
8119         getTop : function(local) {
8120             if(!local){
8121                 return this.getY();
8122             }else{
8123                 return parseInt(this.getStyle("top"), 10) || 0;
8124             }
8125         },
8126
8127         /**
8128          * Gets the bottom Y coordinate of the element (element Y position + element height)
8129          * @param {Boolean} local True to get the local css position instead of page coordinate
8130          * @return {Number}
8131          */
8132         getBottom : function(local){
8133             if(!local){
8134                 return this.getY() + this.getHeight();
8135             }else{
8136                 return (this.getTop(true) + this.getHeight()) || 0;
8137             }
8138         },
8139
8140         /**
8141         * Initializes positioning on this element. If a desired position is not passed, it will make the
8142         * the element positioned relative IF it is not already positioned.
8143         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8144         * @param {Number} zIndex (optional) The zIndex to apply
8145         * @param {Number} x (optional) Set the page X position
8146         * @param {Number} y (optional) Set the page Y position
8147         */
8148         position : function(pos, zIndex, x, y){
8149             if(!pos){
8150                if(this.getStyle('position') == 'static'){
8151                    this.setStyle('position', 'relative');
8152                }
8153             }else{
8154                 this.setStyle("position", pos);
8155             }
8156             if(zIndex){
8157                 this.setStyle("z-index", zIndex);
8158             }
8159             if(x !== undefined && y !== undefined){
8160                 this.setXY([x, y]);
8161             }else if(x !== undefined){
8162                 this.setX(x);
8163             }else if(y !== undefined){
8164                 this.setY(y);
8165             }
8166         },
8167
8168         /**
8169         * Clear positioning back to the default when the document was loaded
8170         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8171         * @return {Roo.Element} this
8172          */
8173         clearPositioning : function(value){
8174             value = value ||'';
8175             this.setStyle({
8176                 "left": value,
8177                 "right": value,
8178                 "top": value,
8179                 "bottom": value,
8180                 "z-index": "",
8181                 "position" : "static"
8182             });
8183             return this;
8184         },
8185
8186         /**
8187         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8188         * snapshot before performing an update and then restoring the element.
8189         * @return {Object}
8190         */
8191         getPositioning : function(){
8192             var l = this.getStyle("left");
8193             var t = this.getStyle("top");
8194             return {
8195                 "position" : this.getStyle("position"),
8196                 "left" : l,
8197                 "right" : l ? "" : this.getStyle("right"),
8198                 "top" : t,
8199                 "bottom" : t ? "" : this.getStyle("bottom"),
8200                 "z-index" : this.getStyle("z-index")
8201             };
8202         },
8203
8204         /**
8205          * Gets the width of the border(s) for the specified side(s)
8206          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8207          * passing lr would get the border (l)eft width + the border (r)ight width.
8208          * @return {Number} The width of the sides passed added together
8209          */
8210         getBorderWidth : function(side){
8211             return this.addStyles(side, El.borders);
8212         },
8213
8214         /**
8215          * Gets the width of the padding(s) for the specified side(s)
8216          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8217          * passing lr would get the padding (l)eft + the padding (r)ight.
8218          * @return {Number} The padding of the sides passed added together
8219          */
8220         getPadding : function(side){
8221             return this.addStyles(side, El.paddings);
8222         },
8223
8224         /**
8225         * Set positioning with an object returned by getPositioning().
8226         * @param {Object} posCfg
8227         * @return {Roo.Element} this
8228          */
8229         setPositioning : function(pc){
8230             this.applyStyles(pc);
8231             if(pc.right == "auto"){
8232                 this.dom.style.right = "";
8233             }
8234             if(pc.bottom == "auto"){
8235                 this.dom.style.bottom = "";
8236             }
8237             return this;
8238         },
8239
8240         // private
8241         fixDisplay : function(){
8242             if(this.getStyle("display") == "none"){
8243                 this.setStyle("visibility", "hidden");
8244                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8245                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8246                     this.setStyle("display", "block");
8247                 }
8248             }
8249         },
8250
8251         /**
8252          * Quick set left and top adding default units
8253          * @param {String} left The left CSS property value
8254          * @param {String} top The top CSS property value
8255          * @return {Roo.Element} this
8256          */
8257          setLeftTop : function(left, top){
8258             this.dom.style.left = this.addUnits(left);
8259             this.dom.style.top = this.addUnits(top);
8260             return this;
8261         },
8262
8263         /**
8264          * Move this element relative to its current position.
8265          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8266          * @param {Number} distance How far to move the element in pixels
8267          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8268          * @return {Roo.Element} this
8269          */
8270          move : function(direction, distance, animate){
8271             var xy = this.getXY();
8272             direction = direction.toLowerCase();
8273             switch(direction){
8274                 case "l":
8275                 case "left":
8276                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8277                     break;
8278                case "r":
8279                case "right":
8280                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8281                     break;
8282                case "t":
8283                case "top":
8284                case "up":
8285                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8286                     break;
8287                case "b":
8288                case "bottom":
8289                case "down":
8290                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8291                     break;
8292             }
8293             return this;
8294         },
8295
8296         /**
8297          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8298          * @return {Roo.Element} this
8299          */
8300         clip : function(){
8301             if(!this.isClipped){
8302                this.isClipped = true;
8303                this.originalClip = {
8304                    "o": this.getStyle("overflow"),
8305                    "x": this.getStyle("overflow-x"),
8306                    "y": this.getStyle("overflow-y")
8307                };
8308                this.setStyle("overflow", "hidden");
8309                this.setStyle("overflow-x", "hidden");
8310                this.setStyle("overflow-y", "hidden");
8311             }
8312             return this;
8313         },
8314
8315         /**
8316          *  Return clipping (overflow) to original clipping before clip() was called
8317          * @return {Roo.Element} this
8318          */
8319         unclip : function(){
8320             if(this.isClipped){
8321                 this.isClipped = false;
8322                 var o = this.originalClip;
8323                 if(o.o){this.setStyle("overflow", o.o);}
8324                 if(o.x){this.setStyle("overflow-x", o.x);}
8325                 if(o.y){this.setStyle("overflow-y", o.y);}
8326             }
8327             return this;
8328         },
8329
8330
8331         /**
8332          * Gets the x,y coordinates specified by the anchor position on the element.
8333          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8334          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8335          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8336          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8337          * @return {Array} [x, y] An array containing the element's x and y coordinates
8338          */
8339         getAnchorXY : function(anchor, local, s){
8340             //Passing a different size is useful for pre-calculating anchors,
8341             //especially for anchored animations that change the el size.
8342
8343             var w, h, vp = false;
8344             if(!s){
8345                 var d = this.dom;
8346                 if(d == document.body || d == document){
8347                     vp = true;
8348                     w = D.getViewWidth(); h = D.getViewHeight();
8349                 }else{
8350                     w = this.getWidth(); h = this.getHeight();
8351                 }
8352             }else{
8353                 w = s.width;  h = s.height;
8354             }
8355             var x = 0, y = 0, r = Math.round;
8356             switch((anchor || "tl").toLowerCase()){
8357                 case "c":
8358                     x = r(w*.5);
8359                     y = r(h*.5);
8360                 break;
8361                 case "t":
8362                     x = r(w*.5);
8363                     y = 0;
8364                 break;
8365                 case "l":
8366                     x = 0;
8367                     y = r(h*.5);
8368                 break;
8369                 case "r":
8370                     x = w;
8371                     y = r(h*.5);
8372                 break;
8373                 case "b":
8374                     x = r(w*.5);
8375                     y = h;
8376                 break;
8377                 case "tl":
8378                     x = 0;
8379                     y = 0;
8380                 break;
8381                 case "bl":
8382                     x = 0;
8383                     y = h;
8384                 break;
8385                 case "br":
8386                     x = w;
8387                     y = h;
8388                 break;
8389                 case "tr":
8390                     x = w;
8391                     y = 0;
8392                 break;
8393             }
8394             if(local === true){
8395                 return [x, y];
8396             }
8397             if(vp){
8398                 var sc = this.getScroll();
8399                 return [x + sc.left, y + sc.top];
8400             }
8401             //Add the element's offset xy
8402             var o = this.getXY();
8403             return [x+o[0], y+o[1]];
8404         },
8405
8406         /**
8407          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8408          * supported position values.
8409          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8410          * @param {String} position The position to align to.
8411          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8412          * @return {Array} [x, y]
8413          */
8414         getAlignToXY : function(el, p, o){
8415             el = Roo.get(el);
8416             var d = this.dom;
8417             if(!el.dom){
8418                 throw "Element.alignTo with an element that doesn't exist";
8419             }
8420             var c = false; //constrain to viewport
8421             var p1 = "", p2 = "";
8422             o = o || [0,0];
8423
8424             if(!p){
8425                 p = "tl-bl";
8426             }else if(p == "?"){
8427                 p = "tl-bl?";
8428             }else if(p.indexOf("-") == -1){
8429                 p = "tl-" + p;
8430             }
8431             p = p.toLowerCase();
8432             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8433             if(!m){
8434                throw "Element.alignTo with an invalid alignment " + p;
8435             }
8436             p1 = m[1]; p2 = m[2]; c = !!m[3];
8437
8438             //Subtract the aligned el's internal xy from the target's offset xy
8439             //plus custom offset to get the aligned el's new offset xy
8440             var a1 = this.getAnchorXY(p1, true);
8441             var a2 = el.getAnchorXY(p2, false);
8442             var x = a2[0] - a1[0] + o[0];
8443             var y = a2[1] - a1[1] + o[1];
8444             if(c){
8445                 //constrain the aligned el to viewport if necessary
8446                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8447                 // 5px of margin for ie
8448                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8449
8450                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8451                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8452                 //otherwise swap the aligned el to the opposite border of the target.
8453                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8454                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8455                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8456                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8457
8458                var doc = document;
8459                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8460                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8461
8462                if((x+w) > dw + scrollX){
8463                     x = swapX ? r.left-w : dw+scrollX-w;
8464                 }
8465                if(x < scrollX){
8466                    x = swapX ? r.right : scrollX;
8467                }
8468                if((y+h) > dh + scrollY){
8469                     y = swapY ? r.top-h : dh+scrollY-h;
8470                 }
8471                if (y < scrollY){
8472                    y = swapY ? r.bottom : scrollY;
8473                }
8474             }
8475             return [x,y];
8476         },
8477
8478         // private
8479         getConstrainToXY : function(){
8480             var os = {top:0, left:0, bottom:0, right: 0};
8481
8482             return function(el, local, offsets, proposedXY){
8483                 el = Roo.get(el);
8484                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8485
8486                 var vw, vh, vx = 0, vy = 0;
8487                 if(el.dom == document.body || el.dom == document){
8488                     vw = Roo.lib.Dom.getViewWidth();
8489                     vh = Roo.lib.Dom.getViewHeight();
8490                 }else{
8491                     vw = el.dom.clientWidth;
8492                     vh = el.dom.clientHeight;
8493                     if(!local){
8494                         var vxy = el.getXY();
8495                         vx = vxy[0];
8496                         vy = vxy[1];
8497                     }
8498                 }
8499
8500                 var s = el.getScroll();
8501
8502                 vx += offsets.left + s.left;
8503                 vy += offsets.top + s.top;
8504
8505                 vw -= offsets.right;
8506                 vh -= offsets.bottom;
8507
8508                 var vr = vx+vw;
8509                 var vb = vy+vh;
8510
8511                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8512                 var x = xy[0], y = xy[1];
8513                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8514
8515                 // only move it if it needs it
8516                 var moved = false;
8517
8518                 // first validate right/bottom
8519                 if((x + w) > vr){
8520                     x = vr - w;
8521                     moved = true;
8522                 }
8523                 if((y + h) > vb){
8524                     y = vb - h;
8525                     moved = true;
8526                 }
8527                 // then make sure top/left isn't negative
8528                 if(x < vx){
8529                     x = vx;
8530                     moved = true;
8531                 }
8532                 if(y < vy){
8533                     y = vy;
8534                     moved = true;
8535                 }
8536                 return moved ? [x, y] : false;
8537             };
8538         }(),
8539
8540         // private
8541         adjustForConstraints : function(xy, parent, offsets){
8542             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8543         },
8544
8545         /**
8546          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8547          * document it aligns it to the viewport.
8548          * The position parameter is optional, and can be specified in any one of the following formats:
8549          * <ul>
8550          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8551          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8552          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8553          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8554          *   <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
8555          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8556          * </ul>
8557          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8558          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8559          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8560          * that specified in order to enforce the viewport constraints.
8561          * Following are all of the supported anchor positions:
8562     <pre>
8563     Value  Description
8564     -----  -----------------------------
8565     tl     The top left corner (default)
8566     t      The center of the top edge
8567     tr     The top right corner
8568     l      The center of the left edge
8569     c      In the center of the element
8570     r      The center of the right edge
8571     bl     The bottom left corner
8572     b      The center of the bottom edge
8573     br     The bottom right corner
8574     </pre>
8575     Example Usage:
8576     <pre><code>
8577     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8578     el.alignTo("other-el");
8579
8580     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8581     el.alignTo("other-el", "tr?");
8582
8583     // align the bottom right corner of el with the center left edge of other-el
8584     el.alignTo("other-el", "br-l?");
8585
8586     // align the center of el with the bottom left corner of other-el and
8587     // adjust the x position by -6 pixels (and the y position by 0)
8588     el.alignTo("other-el", "c-bl", [-6, 0]);
8589     </code></pre>
8590          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8591          * @param {String} position The position to align to.
8592          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8593          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8594          * @return {Roo.Element} this
8595          */
8596         alignTo : function(element, position, offsets, animate){
8597             var xy = this.getAlignToXY(element, position, offsets);
8598             this.setXY(xy, this.preanim(arguments, 3));
8599             return this;
8600         },
8601
8602         /**
8603          * Anchors an element to another element and realigns it when the window is resized.
8604          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8605          * @param {String} position The position to align to.
8606          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8607          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8608          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8609          * is a number, it is used as the buffer delay (defaults to 50ms).
8610          * @param {Function} callback The function to call after the animation finishes
8611          * @return {Roo.Element} this
8612          */
8613         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8614             var action = function(){
8615                 this.alignTo(el, alignment, offsets, animate);
8616                 Roo.callback(callback, this);
8617             };
8618             Roo.EventManager.onWindowResize(action, this);
8619             var tm = typeof monitorScroll;
8620             if(tm != 'undefined'){
8621                 Roo.EventManager.on(window, 'scroll', action, this,
8622                     {buffer: tm == 'number' ? monitorScroll : 50});
8623             }
8624             action.call(this); // align immediately
8625             return this;
8626         },
8627         /**
8628          * Clears any opacity settings from this element. Required in some cases for IE.
8629          * @return {Roo.Element} this
8630          */
8631         clearOpacity : function(){
8632             if (window.ActiveXObject) {
8633                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8634                     this.dom.style.filter = "";
8635                 }
8636             } else {
8637                 this.dom.style.opacity = "";
8638                 this.dom.style["-moz-opacity"] = "";
8639                 this.dom.style["-khtml-opacity"] = "";
8640             }
8641             return this;
8642         },
8643
8644         /**
8645          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8646          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8647          * @return {Roo.Element} this
8648          */
8649         hide : function(animate){
8650             this.setVisible(false, this.preanim(arguments, 0));
8651             return this;
8652         },
8653
8654         /**
8655         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8656         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8657          * @return {Roo.Element} this
8658          */
8659         show : function(animate){
8660             this.setVisible(true, this.preanim(arguments, 0));
8661             return this;
8662         },
8663
8664         /**
8665          * @private Test if size has a unit, otherwise appends the default
8666          */
8667         addUnits : function(size){
8668             return Roo.Element.addUnits(size, this.defaultUnit);
8669         },
8670
8671         /**
8672          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8673          * @return {Roo.Element} this
8674          */
8675         beginMeasure : function(){
8676             var el = this.dom;
8677             if(el.offsetWidth || el.offsetHeight){
8678                 return this; // offsets work already
8679             }
8680             var changed = [];
8681             var p = this.dom, b = document.body; // start with this element
8682             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8683                 var pe = Roo.get(p);
8684                 if(pe.getStyle('display') == 'none'){
8685                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8686                     p.style.visibility = "hidden";
8687                     p.style.display = "block";
8688                 }
8689                 p = p.parentNode;
8690             }
8691             this._measureChanged = changed;
8692             return this;
8693
8694         },
8695
8696         /**
8697          * Restores displays to before beginMeasure was called
8698          * @return {Roo.Element} this
8699          */
8700         endMeasure : function(){
8701             var changed = this._measureChanged;
8702             if(changed){
8703                 for(var i = 0, len = changed.length; i < len; i++) {
8704                     var r = changed[i];
8705                     r.el.style.visibility = r.visibility;
8706                     r.el.style.display = "none";
8707                 }
8708                 this._measureChanged = null;
8709             }
8710             return this;
8711         },
8712
8713         /**
8714         * Update the innerHTML of this element, optionally searching for and processing scripts
8715         * @param {String} html The new HTML
8716         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8717         * @param {Function} callback For async script loading you can be noticed when the update completes
8718         * @return {Roo.Element} this
8719          */
8720         update : function(html, loadScripts, callback){
8721             if(typeof html == "undefined"){
8722                 html = "";
8723             }
8724             if(loadScripts !== true){
8725                 this.dom.innerHTML = html;
8726                 if(typeof callback == "function"){
8727                     callback();
8728                 }
8729                 return this;
8730             }
8731             var id = Roo.id();
8732             var dom = this.dom;
8733
8734             html += '<span id="' + id + '"></span>';
8735
8736             E.onAvailable(id, function(){
8737                 var hd = document.getElementsByTagName("head")[0];
8738                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8739                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8740                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8741
8742                 var match;
8743                 while(match = re.exec(html)){
8744                     var attrs = match[1];
8745                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8746                     if(srcMatch && srcMatch[2]){
8747                        var s = document.createElement("script");
8748                        s.src = srcMatch[2];
8749                        var typeMatch = attrs.match(typeRe);
8750                        if(typeMatch && typeMatch[2]){
8751                            s.type = typeMatch[2];
8752                        }
8753                        hd.appendChild(s);
8754                     }else if(match[2] && match[2].length > 0){
8755                         if(window.execScript) {
8756                            window.execScript(match[2]);
8757                         } else {
8758                             /**
8759                              * eval:var:id
8760                              * eval:var:dom
8761                              * eval:var:html
8762                              * 
8763                              */
8764                            window.eval(match[2]);
8765                         }
8766                     }
8767                 }
8768                 var el = document.getElementById(id);
8769                 if(el){el.parentNode.removeChild(el);}
8770                 if(typeof callback == "function"){
8771                     callback();
8772                 }
8773             });
8774             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8775             return this;
8776         },
8777
8778         /**
8779          * Direct access to the UpdateManager update() method (takes the same parameters).
8780          * @param {String/Function} url The url for this request or a function to call to get the url
8781          * @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}
8782          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8783          * @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.
8784          * @return {Roo.Element} this
8785          */
8786         load : function(){
8787             var um = this.getUpdateManager();
8788             um.update.apply(um, arguments);
8789             return this;
8790         },
8791
8792         /**
8793         * Gets this element's UpdateManager
8794         * @return {Roo.UpdateManager} The UpdateManager
8795         */
8796         getUpdateManager : function(){
8797             if(!this.updateManager){
8798                 this.updateManager = new Roo.UpdateManager(this);
8799             }
8800             return this.updateManager;
8801         },
8802
8803         /**
8804          * Disables text selection for this element (normalized across browsers)
8805          * @return {Roo.Element} this
8806          */
8807         unselectable : function(){
8808             this.dom.unselectable = "on";
8809             this.swallowEvent("selectstart", true);
8810             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8811             this.addClass("x-unselectable");
8812             return this;
8813         },
8814
8815         /**
8816         * Calculates the x, y to center this element on the screen
8817         * @return {Array} The x, y values [x, y]
8818         */
8819         getCenterXY : function(){
8820             return this.getAlignToXY(document, 'c-c');
8821         },
8822
8823         /**
8824         * Centers the Element in either the viewport, or another Element.
8825         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8826         */
8827         center : function(centerIn){
8828             this.alignTo(centerIn || document, 'c-c');
8829             return this;
8830         },
8831
8832         /**
8833          * Tests various css rules/browsers to determine if this element uses a border box
8834          * @return {Boolean}
8835          */
8836         isBorderBox : function(){
8837             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8838         },
8839
8840         /**
8841          * Return a box {x, y, width, height} that can be used to set another elements
8842          * size/location to match this element.
8843          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8844          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8845          * @return {Object} box An object in the format {x, y, width, height}
8846          */
8847         getBox : function(contentBox, local){
8848             var xy;
8849             if(!local){
8850                 xy = this.getXY();
8851             }else{
8852                 var left = parseInt(this.getStyle("left"), 10) || 0;
8853                 var top = parseInt(this.getStyle("top"), 10) || 0;
8854                 xy = [left, top];
8855             }
8856             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8857             if(!contentBox){
8858                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8859             }else{
8860                 var l = this.getBorderWidth("l")+this.getPadding("l");
8861                 var r = this.getBorderWidth("r")+this.getPadding("r");
8862                 var t = this.getBorderWidth("t")+this.getPadding("t");
8863                 var b = this.getBorderWidth("b")+this.getPadding("b");
8864                 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)};
8865             }
8866             bx.right = bx.x + bx.width;
8867             bx.bottom = bx.y + bx.height;
8868             return bx;
8869         },
8870
8871         /**
8872          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8873          for more information about the sides.
8874          * @param {String} sides
8875          * @return {Number}
8876          */
8877         getFrameWidth : function(sides, onlyContentBox){
8878             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8879         },
8880
8881         /**
8882          * 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.
8883          * @param {Object} box The box to fill {x, y, width, height}
8884          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8885          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8886          * @return {Roo.Element} this
8887          */
8888         setBox : function(box, adjust, animate){
8889             var w = box.width, h = box.height;
8890             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8891                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8892                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8893             }
8894             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8895             return this;
8896         },
8897
8898         /**
8899          * Forces the browser to repaint this element
8900          * @return {Roo.Element} this
8901          */
8902          repaint : function(){
8903             var dom = this.dom;
8904             this.addClass("x-repaint");
8905             setTimeout(function(){
8906                 Roo.get(dom).removeClass("x-repaint");
8907             }, 1);
8908             return this;
8909         },
8910
8911         /**
8912          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8913          * then it returns the calculated width of the sides (see getPadding)
8914          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8915          * @return {Object/Number}
8916          */
8917         getMargins : function(side){
8918             if(!side){
8919                 return {
8920                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8921                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8922                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8923                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8924                 };
8925             }else{
8926                 return this.addStyles(side, El.margins);
8927              }
8928         },
8929
8930         // private
8931         addStyles : function(sides, styles){
8932             var val = 0, v, w;
8933             for(var i = 0, len = sides.length; i < len; i++){
8934                 v = this.getStyle(styles[sides.charAt(i)]);
8935                 if(v){
8936                      w = parseInt(v, 10);
8937                      if(w){ val += w; }
8938                 }
8939             }
8940             return val;
8941         },
8942
8943         /**
8944          * Creates a proxy element of this element
8945          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8946          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8947          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8948          * @return {Roo.Element} The new proxy element
8949          */
8950         createProxy : function(config, renderTo, matchBox){
8951             if(renderTo){
8952                 renderTo = Roo.getDom(renderTo);
8953             }else{
8954                 renderTo = document.body;
8955             }
8956             config = typeof config == "object" ?
8957                 config : {tag : "div", cls: config};
8958             var proxy = Roo.DomHelper.append(renderTo, config, true);
8959             if(matchBox){
8960                proxy.setBox(this.getBox());
8961             }
8962             return proxy;
8963         },
8964
8965         /**
8966          * Puts a mask over this element to disable user interaction. Requires core.css.
8967          * This method can only be applied to elements which accept child nodes.
8968          * @param {String} msg (optional) A message to display in the mask
8969          * @param {String} msgCls (optional) A css class to apply to the msg element
8970          * @return {Element} The mask  element
8971          */
8972         mask : function(msg, msgCls)
8973         {
8974             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8975                 this.setStyle("position", "relative");
8976             }
8977             if(!this._mask){
8978                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8979             }
8980             this.addClass("x-masked");
8981             this._mask.setDisplayed(true);
8982             
8983             // we wander
8984             var z = 0;
8985             var dom = this.dom
8986             while (dom && dom.style) {
8987                 if (!isNaN(parseInt(dom.style.zIndex))) {
8988                     z = Math.max(z, parseInt(dom.style.zIndex));
8989                 }
8990                 dom = dom.parentNode;
8991             }
8992             // if we are masking the body - then it hides everything..
8993             if (this.dom == document.body) {
8994                 z = 1000000;
8995                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8996                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8997             }
8998            
8999             if(typeof msg == 'string'){
9000                 if(!this._maskMsg){
9001                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9002                 }
9003                 var mm = this._maskMsg;
9004                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9005                 mm.dom.firstChild.innerHTML = msg;
9006                 mm.setDisplayed(true);
9007                 mm.center(this);
9008                 mm.setStyle('z-index', z + 102);
9009             }
9010             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9011                 this._mask.setHeight(this.getHeight());
9012             }
9013             this._mask.setStyle('z-index', z + 100);
9014             
9015             return this._mask;
9016         },
9017
9018         /**
9019          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9020          * it is cached for reuse.
9021          */
9022         unmask : function(removeEl){
9023             if(this._mask){
9024                 if(removeEl === true){
9025                     this._mask.remove();
9026                     delete this._mask;
9027                     if(this._maskMsg){
9028                         this._maskMsg.remove();
9029                         delete this._maskMsg;
9030                     }
9031                 }else{
9032                     this._mask.setDisplayed(false);
9033                     if(this._maskMsg){
9034                         this._maskMsg.setDisplayed(false);
9035                     }
9036                 }
9037             }
9038             this.removeClass("x-masked");
9039         },
9040
9041         /**
9042          * Returns true if this element is masked
9043          * @return {Boolean}
9044          */
9045         isMasked : function(){
9046             return this._mask && this._mask.isVisible();
9047         },
9048
9049         /**
9050          * Creates an iframe shim for this element to keep selects and other windowed objects from
9051          * showing through.
9052          * @return {Roo.Element} The new shim element
9053          */
9054         createShim : function(){
9055             var el = document.createElement('iframe');
9056             el.frameBorder = 'no';
9057             el.className = 'roo-shim';
9058             if(Roo.isIE && Roo.isSecure){
9059                 el.src = Roo.SSL_SECURE_URL;
9060             }
9061             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9062             shim.autoBoxAdjust = false;
9063             return shim;
9064         },
9065
9066         /**
9067          * Removes this element from the DOM and deletes it from the cache
9068          */
9069         remove : function(){
9070             if(this.dom.parentNode){
9071                 this.dom.parentNode.removeChild(this.dom);
9072             }
9073             delete El.cache[this.dom.id];
9074         },
9075
9076         /**
9077          * Sets up event handlers to add and remove a css class when the mouse is over this element
9078          * @param {String} className
9079          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9080          * mouseout events for children elements
9081          * @return {Roo.Element} this
9082          */
9083         addClassOnOver : function(className, preventFlicker){
9084             this.on("mouseover", function(){
9085                 Roo.fly(this, '_internal').addClass(className);
9086             }, this.dom);
9087             var removeFn = function(e){
9088                 if(preventFlicker !== true || !e.within(this, true)){
9089                     Roo.fly(this, '_internal').removeClass(className);
9090                 }
9091             };
9092             this.on("mouseout", removeFn, this.dom);
9093             return this;
9094         },
9095
9096         /**
9097          * Sets up event handlers to add and remove a css class when this element has the focus
9098          * @param {String} className
9099          * @return {Roo.Element} this
9100          */
9101         addClassOnFocus : function(className){
9102             this.on("focus", function(){
9103                 Roo.fly(this, '_internal').addClass(className);
9104             }, this.dom);
9105             this.on("blur", function(){
9106                 Roo.fly(this, '_internal').removeClass(className);
9107             }, this.dom);
9108             return this;
9109         },
9110         /**
9111          * 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)
9112          * @param {String} className
9113          * @return {Roo.Element} this
9114          */
9115         addClassOnClick : function(className){
9116             var dom = this.dom;
9117             this.on("mousedown", function(){
9118                 Roo.fly(dom, '_internal').addClass(className);
9119                 var d = Roo.get(document);
9120                 var fn = function(){
9121                     Roo.fly(dom, '_internal').removeClass(className);
9122                     d.removeListener("mouseup", fn);
9123                 };
9124                 d.on("mouseup", fn);
9125             });
9126             return this;
9127         },
9128
9129         /**
9130          * Stops the specified event from bubbling and optionally prevents the default action
9131          * @param {String} eventName
9132          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9133          * @return {Roo.Element} this
9134          */
9135         swallowEvent : function(eventName, preventDefault){
9136             var fn = function(e){
9137                 e.stopPropagation();
9138                 if(preventDefault){
9139                     e.preventDefault();
9140                 }
9141             };
9142             if(eventName instanceof Array){
9143                 for(var i = 0, len = eventName.length; i < len; i++){
9144                      this.on(eventName[i], fn);
9145                 }
9146                 return this;
9147             }
9148             this.on(eventName, fn);
9149             return this;
9150         },
9151
9152         /**
9153          * @private
9154          */
9155       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9156
9157         /**
9158          * Sizes this element to its parent element's dimensions performing
9159          * neccessary box adjustments.
9160          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9161          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9162          * @return {Roo.Element} this
9163          */
9164         fitToParent : function(monitorResize, targetParent) {
9165           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9166           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9167           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9168             return;
9169           }
9170           var p = Roo.get(targetParent || this.dom.parentNode);
9171           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9172           if (monitorResize === true) {
9173             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9174             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9175           }
9176           return this;
9177         },
9178
9179         /**
9180          * Gets the next sibling, skipping text nodes
9181          * @return {HTMLElement} The next sibling or null
9182          */
9183         getNextSibling : function(){
9184             var n = this.dom.nextSibling;
9185             while(n && n.nodeType != 1){
9186                 n = n.nextSibling;
9187             }
9188             return n;
9189         },
9190
9191         /**
9192          * Gets the previous sibling, skipping text nodes
9193          * @return {HTMLElement} The previous sibling or null
9194          */
9195         getPrevSibling : function(){
9196             var n = this.dom.previousSibling;
9197             while(n && n.nodeType != 1){
9198                 n = n.previousSibling;
9199             }
9200             return n;
9201         },
9202
9203
9204         /**
9205          * Appends the passed element(s) to this element
9206          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9207          * @return {Roo.Element} this
9208          */
9209         appendChild: function(el){
9210             el = Roo.get(el);
9211             el.appendTo(this);
9212             return this;
9213         },
9214
9215         /**
9216          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9217          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9218          * automatically generated with the specified attributes.
9219          * @param {HTMLElement} insertBefore (optional) a child element of this element
9220          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9221          * @return {Roo.Element} The new child element
9222          */
9223         createChild: function(config, insertBefore, returnDom){
9224             config = config || {tag:'div'};
9225             if(insertBefore){
9226                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9227             }
9228             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9229         },
9230
9231         /**
9232          * Appends this element to the passed element
9233          * @param {String/HTMLElement/Element} el The new parent element
9234          * @return {Roo.Element} this
9235          */
9236         appendTo: function(el){
9237             el = Roo.getDom(el);
9238             el.appendChild(this.dom);
9239             return this;
9240         },
9241
9242         /**
9243          * Inserts this element before the passed element in the DOM
9244          * @param {String/HTMLElement/Element} el The element to insert before
9245          * @return {Roo.Element} this
9246          */
9247         insertBefore: function(el){
9248             el = Roo.getDom(el);
9249             el.parentNode.insertBefore(this.dom, el);
9250             return this;
9251         },
9252
9253         /**
9254          * Inserts this element after the passed element in the DOM
9255          * @param {String/HTMLElement/Element} el The element to insert after
9256          * @return {Roo.Element} this
9257          */
9258         insertAfter: function(el){
9259             el = Roo.getDom(el);
9260             el.parentNode.insertBefore(this.dom, el.nextSibling);
9261             return this;
9262         },
9263
9264         /**
9265          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9266          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9267          * @return {Roo.Element} The new child
9268          */
9269         insertFirst: function(el, returnDom){
9270             el = el || {};
9271             if(typeof el == 'object' && !el.nodeType){ // dh config
9272                 return this.createChild(el, this.dom.firstChild, returnDom);
9273             }else{
9274                 el = Roo.getDom(el);
9275                 this.dom.insertBefore(el, this.dom.firstChild);
9276                 return !returnDom ? Roo.get(el) : el;
9277             }
9278         },
9279
9280         /**
9281          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9282          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9283          * @param {String} where (optional) 'before' or 'after' defaults to before
9284          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9285          * @return {Roo.Element} the inserted Element
9286          */
9287         insertSibling: function(el, where, returnDom){
9288             where = where ? where.toLowerCase() : 'before';
9289             el = el || {};
9290             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9291
9292             if(typeof el == 'object' && !el.nodeType){ // dh config
9293                 if(where == 'after' && !this.dom.nextSibling){
9294                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9295                 }else{
9296                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9297                 }
9298
9299             }else{
9300                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9301                             where == 'before' ? this.dom : this.dom.nextSibling);
9302                 if(!returnDom){
9303                     rt = Roo.get(rt);
9304                 }
9305             }
9306             return rt;
9307         },
9308
9309         /**
9310          * Creates and wraps this element with another element
9311          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9312          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9313          * @return {HTMLElement/Element} The newly created wrapper element
9314          */
9315         wrap: function(config, returnDom){
9316             if(!config){
9317                 config = {tag: "div"};
9318             }
9319             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9320             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9321             return newEl;
9322         },
9323
9324         /**
9325          * Replaces the passed element with this element
9326          * @param {String/HTMLElement/Element} el The element to replace
9327          * @return {Roo.Element} this
9328          */
9329         replace: function(el){
9330             el = Roo.get(el);
9331             this.insertBefore(el);
9332             el.remove();
9333             return this;
9334         },
9335
9336         /**
9337          * Inserts an html fragment into this element
9338          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9339          * @param {String} html The HTML fragment
9340          * @param {Boolean} returnEl True to return an Roo.Element
9341          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9342          */
9343         insertHtml : function(where, html, returnEl){
9344             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9345             return returnEl ? Roo.get(el) : el;
9346         },
9347
9348         /**
9349          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9350          * @param {Object} o The object with the attributes
9351          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9352          * @return {Roo.Element} this
9353          */
9354         set : function(o, useSet){
9355             var el = this.dom;
9356             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9357             for(var attr in o){
9358                 if(attr == "style" || typeof o[attr] == "function") continue;
9359                 if(attr=="cls"){
9360                     el.className = o["cls"];
9361                 }else{
9362                     if(useSet) el.setAttribute(attr, o[attr]);
9363                     else el[attr] = o[attr];
9364                 }
9365             }
9366             if(o.style){
9367                 Roo.DomHelper.applyStyles(el, o.style);
9368             }
9369             return this;
9370         },
9371
9372         /**
9373          * Convenience method for constructing a KeyMap
9374          * @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:
9375          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9376          * @param {Function} fn The function to call
9377          * @param {Object} scope (optional) The scope of the function
9378          * @return {Roo.KeyMap} The KeyMap created
9379          */
9380         addKeyListener : function(key, fn, scope){
9381             var config;
9382             if(typeof key != "object" || key instanceof Array){
9383                 config = {
9384                     key: key,
9385                     fn: fn,
9386                     scope: scope
9387                 };
9388             }else{
9389                 config = {
9390                     key : key.key,
9391                     shift : key.shift,
9392                     ctrl : key.ctrl,
9393                     alt : key.alt,
9394                     fn: fn,
9395                     scope: scope
9396                 };
9397             }
9398             return new Roo.KeyMap(this, config);
9399         },
9400
9401         /**
9402          * Creates a KeyMap for this element
9403          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9404          * @return {Roo.KeyMap} The KeyMap created
9405          */
9406         addKeyMap : function(config){
9407             return new Roo.KeyMap(this, config);
9408         },
9409
9410         /**
9411          * Returns true if this element is scrollable.
9412          * @return {Boolean}
9413          */
9414          isScrollable : function(){
9415             var dom = this.dom;
9416             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9417         },
9418
9419         /**
9420          * 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().
9421          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9422          * @param {Number} value The new scroll value
9423          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9424          * @return {Element} this
9425          */
9426
9427         scrollTo : function(side, value, animate){
9428             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9429             if(!animate || !A){
9430                 this.dom[prop] = value;
9431             }else{
9432                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9433                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9434             }
9435             return this;
9436         },
9437
9438         /**
9439          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9440          * within this element's scrollable range.
9441          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9442          * @param {Number} distance How far to scroll the element in pixels
9443          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9444          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9445          * was scrolled as far as it could go.
9446          */
9447          scroll : function(direction, distance, animate){
9448              if(!this.isScrollable()){
9449                  return;
9450              }
9451              var el = this.dom;
9452              var l = el.scrollLeft, t = el.scrollTop;
9453              var w = el.scrollWidth, h = el.scrollHeight;
9454              var cw = el.clientWidth, ch = el.clientHeight;
9455              direction = direction.toLowerCase();
9456              var scrolled = false;
9457              var a = this.preanim(arguments, 2);
9458              switch(direction){
9459                  case "l":
9460                  case "left":
9461                      if(w - l > cw){
9462                          var v = Math.min(l + distance, w-cw);
9463                          this.scrollTo("left", v, a);
9464                          scrolled = true;
9465                      }
9466                      break;
9467                 case "r":
9468                 case "right":
9469                      if(l > 0){
9470                          var v = Math.max(l - distance, 0);
9471                          this.scrollTo("left", v, a);
9472                          scrolled = true;
9473                      }
9474                      break;
9475                 case "t":
9476                 case "top":
9477                 case "up":
9478                      if(t > 0){
9479                          var v = Math.max(t - distance, 0);
9480                          this.scrollTo("top", v, a);
9481                          scrolled = true;
9482                      }
9483                      break;
9484                 case "b":
9485                 case "bottom":
9486                 case "down":
9487                      if(h - t > ch){
9488                          var v = Math.min(t + distance, h-ch);
9489                          this.scrollTo("top", v, a);
9490                          scrolled = true;
9491                      }
9492                      break;
9493              }
9494              return scrolled;
9495         },
9496
9497         /**
9498          * Translates the passed page coordinates into left/top css values for this element
9499          * @param {Number/Array} x The page x or an array containing [x, y]
9500          * @param {Number} y The page y
9501          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9502          */
9503         translatePoints : function(x, y){
9504             if(typeof x == 'object' || x instanceof Array){
9505                 y = x[1]; x = x[0];
9506             }
9507             var p = this.getStyle('position');
9508             var o = this.getXY();
9509
9510             var l = parseInt(this.getStyle('left'), 10);
9511             var t = parseInt(this.getStyle('top'), 10);
9512
9513             if(isNaN(l)){
9514                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9515             }
9516             if(isNaN(t)){
9517                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9518             }
9519
9520             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9521         },
9522
9523         /**
9524          * Returns the current scroll position of the element.
9525          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9526          */
9527         getScroll : function(){
9528             var d = this.dom, doc = document;
9529             if(d == doc || d == doc.body){
9530                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9531                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9532                 return {left: l, top: t};
9533             }else{
9534                 return {left: d.scrollLeft, top: d.scrollTop};
9535             }
9536         },
9537
9538         /**
9539          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9540          * are convert to standard 6 digit hex color.
9541          * @param {String} attr The css attribute
9542          * @param {String} defaultValue The default value to use when a valid color isn't found
9543          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9544          * YUI color anims.
9545          */
9546         getColor : function(attr, defaultValue, prefix){
9547             var v = this.getStyle(attr);
9548             if(!v || v == "transparent" || v == "inherit") {
9549                 return defaultValue;
9550             }
9551             var color = typeof prefix == "undefined" ? "#" : prefix;
9552             if(v.substr(0, 4) == "rgb("){
9553                 var rvs = v.slice(4, v.length -1).split(",");
9554                 for(var i = 0; i < 3; i++){
9555                     var h = parseInt(rvs[i]).toString(16);
9556                     if(h < 16){
9557                         h = "0" + h;
9558                     }
9559                     color += h;
9560                 }
9561             } else {
9562                 if(v.substr(0, 1) == "#"){
9563                     if(v.length == 4) {
9564                         for(var i = 1; i < 4; i++){
9565                             var c = v.charAt(i);
9566                             color +=  c + c;
9567                         }
9568                     }else if(v.length == 7){
9569                         color += v.substr(1);
9570                     }
9571                 }
9572             }
9573             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9574         },
9575
9576         /**
9577          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9578          * gradient background, rounded corners and a 4-way shadow.
9579          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9580          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9581          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9582          * @return {Roo.Element} this
9583          */
9584         boxWrap : function(cls){
9585             cls = cls || 'x-box';
9586             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9587             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9588             return el;
9589         },
9590
9591         /**
9592          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9593          * @param {String} namespace The namespace in which to look for the attribute
9594          * @param {String} name The attribute name
9595          * @return {String} The attribute value
9596          */
9597         getAttributeNS : Roo.isIE ? function(ns, name){
9598             var d = this.dom;
9599             var type = typeof d[ns+":"+name];
9600             if(type != 'undefined' && type != 'unknown'){
9601                 return d[ns+":"+name];
9602             }
9603             return d[name];
9604         } : function(ns, name){
9605             var d = this.dom;
9606             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9607         },
9608         
9609         
9610         /**
9611          * Sets or Returns the value the dom attribute value
9612          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9613          * @param {String} value (optional) The value to set the attribute to
9614          * @return {String} The attribute value
9615          */
9616         attr : function(name){
9617             if (arguments.length > 1) {
9618                 this.dom.setAttribute(name, arguments[1]);
9619                 return arguments[1];
9620             }
9621             if (typeof(name) == 'object') {
9622                 for(var i in name) {
9623                     this.attr(i, name[i]);
9624                 }
9625                 return name;
9626             }
9627             
9628             
9629             if (!this.dom.hasAttribute(name)) {
9630                 return undefined;
9631             }
9632             return this.dom.getAttribute(name);
9633         }
9634         
9635         
9636         
9637     };
9638
9639     var ep = El.prototype;
9640
9641     /**
9642      * Appends an event handler (Shorthand for addListener)
9643      * @param {String}   eventName     The type of event to append
9644      * @param {Function} fn        The method the event invokes
9645      * @param {Object} scope       (optional) The scope (this object) of the fn
9646      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9647      * @method
9648      */
9649     ep.on = ep.addListener;
9650         // backwards compat
9651     ep.mon = ep.addListener;
9652
9653     /**
9654      * Removes an event handler from this element (shorthand for removeListener)
9655      * @param {String} eventName the type of event to remove
9656      * @param {Function} fn the method the event invokes
9657      * @return {Roo.Element} this
9658      * @method
9659      */
9660     ep.un = ep.removeListener;
9661
9662     /**
9663      * true to automatically adjust width and height settings for box-model issues (default to true)
9664      */
9665     ep.autoBoxAdjust = true;
9666
9667     // private
9668     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9669
9670     // private
9671     El.addUnits = function(v, defaultUnit){
9672         if(v === "" || v == "auto"){
9673             return v;
9674         }
9675         if(v === undefined){
9676             return '';
9677         }
9678         if(typeof v == "number" || !El.unitPattern.test(v)){
9679             return v + (defaultUnit || 'px');
9680         }
9681         return v;
9682     };
9683
9684     // special markup used throughout Roo when box wrapping elements
9685     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>';
9686     /**
9687      * Visibility mode constant - Use visibility to hide element
9688      * @static
9689      * @type Number
9690      */
9691     El.VISIBILITY = 1;
9692     /**
9693      * Visibility mode constant - Use display to hide element
9694      * @static
9695      * @type Number
9696      */
9697     El.DISPLAY = 2;
9698
9699     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9700     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9701     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9702
9703
9704
9705     /**
9706      * @private
9707      */
9708     El.cache = {};
9709
9710     var docEl;
9711
9712     /**
9713      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9714      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9715      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9716      * @return {Element} The Element object
9717      * @static
9718      */
9719     El.get = function(el){
9720         var ex, elm, id;
9721         if(!el){ return null; }
9722         if(typeof el == "string"){ // element id
9723             if(!(elm = document.getElementById(el))){
9724                 return null;
9725             }
9726             if(ex = El.cache[el]){
9727                 ex.dom = elm;
9728             }else{
9729                 ex = El.cache[el] = new El(elm);
9730             }
9731             return ex;
9732         }else if(el.tagName){ // dom element
9733             if(!(id = el.id)){
9734                 id = Roo.id(el);
9735             }
9736             if(ex = El.cache[id]){
9737                 ex.dom = el;
9738             }else{
9739                 ex = El.cache[id] = new El(el);
9740             }
9741             return ex;
9742         }else if(el instanceof El){
9743             if(el != docEl){
9744                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9745                                                               // catch case where it hasn't been appended
9746                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9747             }
9748             return el;
9749         }else if(el.isComposite){
9750             return el;
9751         }else if(el instanceof Array){
9752             return El.select(el);
9753         }else if(el == document){
9754             // create a bogus element object representing the document object
9755             if(!docEl){
9756                 var f = function(){};
9757                 f.prototype = El.prototype;
9758                 docEl = new f();
9759                 docEl.dom = document;
9760             }
9761             return docEl;
9762         }
9763         return null;
9764     };
9765
9766     // private
9767     El.uncache = function(el){
9768         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9769             if(a[i]){
9770                 delete El.cache[a[i].id || a[i]];
9771             }
9772         }
9773     };
9774
9775     // private
9776     // Garbage collection - uncache elements/purge listeners on orphaned elements
9777     // so we don't hold a reference and cause the browser to retain them
9778     El.garbageCollect = function(){
9779         if(!Roo.enableGarbageCollector){
9780             clearInterval(El.collectorThread);
9781             return;
9782         }
9783         for(var eid in El.cache){
9784             var el = El.cache[eid], d = el.dom;
9785             // -------------------------------------------------------
9786             // Determining what is garbage:
9787             // -------------------------------------------------------
9788             // !d
9789             // dom node is null, definitely garbage
9790             // -------------------------------------------------------
9791             // !d.parentNode
9792             // no parentNode == direct orphan, definitely garbage
9793             // -------------------------------------------------------
9794             // !d.offsetParent && !document.getElementById(eid)
9795             // display none elements have no offsetParent so we will
9796             // also try to look it up by it's id. However, check
9797             // offsetParent first so we don't do unneeded lookups.
9798             // This enables collection of elements that are not orphans
9799             // directly, but somewhere up the line they have an orphan
9800             // parent.
9801             // -------------------------------------------------------
9802             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9803                 delete El.cache[eid];
9804                 if(d && Roo.enableListenerCollection){
9805                     E.purgeElement(d);
9806                 }
9807             }
9808         }
9809     }
9810     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9811
9812
9813     // dom is optional
9814     El.Flyweight = function(dom){
9815         this.dom = dom;
9816     };
9817     El.Flyweight.prototype = El.prototype;
9818
9819     El._flyweights = {};
9820     /**
9821      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9822      * the dom node can be overwritten by other code.
9823      * @param {String/HTMLElement} el The dom node or id
9824      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9825      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9826      * @static
9827      * @return {Element} The shared Element object
9828      */
9829     El.fly = function(el, named){
9830         named = named || '_global';
9831         el = Roo.getDom(el);
9832         if(!el){
9833             return null;
9834         }
9835         if(!El._flyweights[named]){
9836             El._flyweights[named] = new El.Flyweight();
9837         }
9838         El._flyweights[named].dom = el;
9839         return El._flyweights[named];
9840     };
9841
9842     /**
9843      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9844      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9845      * Shorthand of {@link Roo.Element#get}
9846      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9847      * @return {Element} The Element object
9848      * @member Roo
9849      * @method get
9850      */
9851     Roo.get = El.get;
9852     /**
9853      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9854      * the dom node can be overwritten by other code.
9855      * Shorthand of {@link Roo.Element#fly}
9856      * @param {String/HTMLElement} el The dom node or id
9857      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9858      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9859      * @static
9860      * @return {Element} The shared Element object
9861      * @member Roo
9862      * @method fly
9863      */
9864     Roo.fly = El.fly;
9865
9866     // speedy lookup for elements never to box adjust
9867     var noBoxAdjust = Roo.isStrict ? {
9868         select:1
9869     } : {
9870         input:1, select:1, textarea:1
9871     };
9872     if(Roo.isIE || Roo.isGecko){
9873         noBoxAdjust['button'] = 1;
9874     }
9875
9876
9877     Roo.EventManager.on(window, 'unload', function(){
9878         delete El.cache;
9879         delete El._flyweights;
9880     });
9881 })();
9882
9883
9884
9885
9886 if(Roo.DomQuery){
9887     Roo.Element.selectorFunction = Roo.DomQuery.select;
9888 }
9889
9890 Roo.Element.select = function(selector, unique, root){
9891     var els;
9892     if(typeof selector == "string"){
9893         els = Roo.Element.selectorFunction(selector, root);
9894     }else if(selector.length !== undefined){
9895         els = selector;
9896     }else{
9897         throw "Invalid selector";
9898     }
9899     if(unique === true){
9900         return new Roo.CompositeElement(els);
9901     }else{
9902         return new Roo.CompositeElementLite(els);
9903     }
9904 };
9905 /**
9906  * Selects elements based on the passed CSS selector to enable working on them as 1.
9907  * @param {String/Array} selector The CSS selector or an array of elements
9908  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9909  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9910  * @return {CompositeElementLite/CompositeElement}
9911  * @member Roo
9912  * @method select
9913  */
9914 Roo.select = Roo.Element.select;
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928
9929 /*
9930  * Based on:
9931  * Ext JS Library 1.1.1
9932  * Copyright(c) 2006-2007, Ext JS, LLC.
9933  *
9934  * Originally Released Under LGPL - original licence link has changed is not relivant.
9935  *
9936  * Fork - LGPL
9937  * <script type="text/javascript">
9938  */
9939
9940
9941
9942 //Notifies Element that fx methods are available
9943 Roo.enableFx = true;
9944
9945 /**
9946  * @class Roo.Fx
9947  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9948  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9949  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9950  * Element effects to work.</p><br/>
9951  *
9952  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9953  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9954  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9955  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9956  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9957  * expected results and should be done with care.</p><br/>
9958  *
9959  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9960  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9961 <pre>
9962 Value  Description
9963 -----  -----------------------------
9964 tl     The top left corner
9965 t      The center of the top edge
9966 tr     The top right corner
9967 l      The center of the left edge
9968 r      The center of the right edge
9969 bl     The bottom left corner
9970 b      The center of the bottom edge
9971 br     The bottom right corner
9972 </pre>
9973  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9974  * below are common options that can be passed to any Fx method.</b>
9975  * @cfg {Function} callback A function called when the effect is finished
9976  * @cfg {Object} scope The scope of the effect function
9977  * @cfg {String} easing A valid Easing value for the effect
9978  * @cfg {String} afterCls A css class to apply after the effect
9979  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9980  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9981  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9982  * effects that end with the element being visually hidden, ignored otherwise)
9983  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9984  * a function which returns such a specification that will be applied to the Element after the effect finishes
9985  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9986  * @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
9987  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9988  */
9989 Roo.Fx = {
9990         /**
9991          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9992          * origin for the slide effect.  This function automatically handles wrapping the element with
9993          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9994          * Usage:
9995          *<pre><code>
9996 // default: slide the element in from the top
9997 el.slideIn();
9998
9999 // custom: slide the element in from the right with a 2-second duration
10000 el.slideIn('r', { duration: 2 });
10001
10002 // common config options shown with default values
10003 el.slideIn('t', {
10004     easing: 'easeOut',
10005     duration: .5
10006 });
10007 </code></pre>
10008          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10009          * @param {Object} options (optional) Object literal with any of the Fx config options
10010          * @return {Roo.Element} The Element
10011          */
10012     slideIn : function(anchor, o){
10013         var el = this.getFxEl();
10014         o = o || {};
10015
10016         el.queueFx(o, function(){
10017
10018             anchor = anchor || "t";
10019
10020             // fix display to visibility
10021             this.fixDisplay();
10022
10023             // restore values after effect
10024             var r = this.getFxRestore();
10025             var b = this.getBox();
10026             // fixed size for slide
10027             this.setSize(b);
10028
10029             // wrap if needed
10030             var wrap = this.fxWrap(r.pos, o, "hidden");
10031
10032             var st = this.dom.style;
10033             st.visibility = "visible";
10034             st.position = "absolute";
10035
10036             // clear out temp styles after slide and unwrap
10037             var after = function(){
10038                 el.fxUnwrap(wrap, r.pos, o);
10039                 st.width = r.width;
10040                 st.height = r.height;
10041                 el.afterFx(o);
10042             };
10043             // time to calc the positions
10044             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10045
10046             switch(anchor.toLowerCase()){
10047                 case "t":
10048                     wrap.setSize(b.width, 0);
10049                     st.left = st.bottom = "0";
10050                     a = {height: bh};
10051                 break;
10052                 case "l":
10053                     wrap.setSize(0, b.height);
10054                     st.right = st.top = "0";
10055                     a = {width: bw};
10056                 break;
10057                 case "r":
10058                     wrap.setSize(0, b.height);
10059                     wrap.setX(b.right);
10060                     st.left = st.top = "0";
10061                     a = {width: bw, points: pt};
10062                 break;
10063                 case "b":
10064                     wrap.setSize(b.width, 0);
10065                     wrap.setY(b.bottom);
10066                     st.left = st.top = "0";
10067                     a = {height: bh, points: pt};
10068                 break;
10069                 case "tl":
10070                     wrap.setSize(0, 0);
10071                     st.right = st.bottom = "0";
10072                     a = {width: bw, height: bh};
10073                 break;
10074                 case "bl":
10075                     wrap.setSize(0, 0);
10076                     wrap.setY(b.y+b.height);
10077                     st.right = st.top = "0";
10078                     a = {width: bw, height: bh, points: pt};
10079                 break;
10080                 case "br":
10081                     wrap.setSize(0, 0);
10082                     wrap.setXY([b.right, b.bottom]);
10083                     st.left = st.top = "0";
10084                     a = {width: bw, height: bh, points: pt};
10085                 break;
10086                 case "tr":
10087                     wrap.setSize(0, 0);
10088                     wrap.setX(b.x+b.width);
10089                     st.left = st.bottom = "0";
10090                     a = {width: bw, height: bh, points: pt};
10091                 break;
10092             }
10093             this.dom.style.visibility = "visible";
10094             wrap.show();
10095
10096             arguments.callee.anim = wrap.fxanim(a,
10097                 o,
10098                 'motion',
10099                 .5,
10100                 'easeOut', after);
10101         });
10102         return this;
10103     },
10104     
10105         /**
10106          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10107          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10108          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10109          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10110          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10111          * Usage:
10112          *<pre><code>
10113 // default: slide the element out to the top
10114 el.slideOut();
10115
10116 // custom: slide the element out to the right with a 2-second duration
10117 el.slideOut('r', { duration: 2 });
10118
10119 // common config options shown with default values
10120 el.slideOut('t', {
10121     easing: 'easeOut',
10122     duration: .5,
10123     remove: false,
10124     useDisplay: false
10125 });
10126 </code></pre>
10127          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10128          * @param {Object} options (optional) Object literal with any of the Fx config options
10129          * @return {Roo.Element} The Element
10130          */
10131     slideOut : function(anchor, o){
10132         var el = this.getFxEl();
10133         o = o || {};
10134
10135         el.queueFx(o, function(){
10136
10137             anchor = anchor || "t";
10138
10139             // restore values after effect
10140             var r = this.getFxRestore();
10141             
10142             var b = this.getBox();
10143             // fixed size for slide
10144             this.setSize(b);
10145
10146             // wrap if needed
10147             var wrap = this.fxWrap(r.pos, o, "visible");
10148
10149             var st = this.dom.style;
10150             st.visibility = "visible";
10151             st.position = "absolute";
10152
10153             wrap.setSize(b);
10154
10155             var after = function(){
10156                 if(o.useDisplay){
10157                     el.setDisplayed(false);
10158                 }else{
10159                     el.hide();
10160                 }
10161
10162                 el.fxUnwrap(wrap, r.pos, o);
10163
10164                 st.width = r.width;
10165                 st.height = r.height;
10166
10167                 el.afterFx(o);
10168             };
10169
10170             var a, zero = {to: 0};
10171             switch(anchor.toLowerCase()){
10172                 case "t":
10173                     st.left = st.bottom = "0";
10174                     a = {height: zero};
10175                 break;
10176                 case "l":
10177                     st.right = st.top = "0";
10178                     a = {width: zero};
10179                 break;
10180                 case "r":
10181                     st.left = st.top = "0";
10182                     a = {width: zero, points: {to:[b.right, b.y]}};
10183                 break;
10184                 case "b":
10185                     st.left = st.top = "0";
10186                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10187                 break;
10188                 case "tl":
10189                     st.right = st.bottom = "0";
10190                     a = {width: zero, height: zero};
10191                 break;
10192                 case "bl":
10193                     st.right = st.top = "0";
10194                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10195                 break;
10196                 case "br":
10197                     st.left = st.top = "0";
10198                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10199                 break;
10200                 case "tr":
10201                     st.left = st.bottom = "0";
10202                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10203                 break;
10204             }
10205
10206             arguments.callee.anim = wrap.fxanim(a,
10207                 o,
10208                 'motion',
10209                 .5,
10210                 "easeOut", after);
10211         });
10212         return this;
10213     },
10214
10215         /**
10216          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10217          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10218          * The element must be removed from the DOM using the 'remove' config option if desired.
10219          * Usage:
10220          *<pre><code>
10221 // default
10222 el.puff();
10223
10224 // common config options shown with default values
10225 el.puff({
10226     easing: 'easeOut',
10227     duration: .5,
10228     remove: false,
10229     useDisplay: false
10230 });
10231 </code></pre>
10232          * @param {Object} options (optional) Object literal with any of the Fx config options
10233          * @return {Roo.Element} The Element
10234          */
10235     puff : function(o){
10236         var el = this.getFxEl();
10237         o = o || {};
10238
10239         el.queueFx(o, function(){
10240             this.clearOpacity();
10241             this.show();
10242
10243             // restore values after effect
10244             var r = this.getFxRestore();
10245             var st = this.dom.style;
10246
10247             var after = function(){
10248                 if(o.useDisplay){
10249                     el.setDisplayed(false);
10250                 }else{
10251                     el.hide();
10252                 }
10253
10254                 el.clearOpacity();
10255
10256                 el.setPositioning(r.pos);
10257                 st.width = r.width;
10258                 st.height = r.height;
10259                 st.fontSize = '';
10260                 el.afterFx(o);
10261             };
10262
10263             var width = this.getWidth();
10264             var height = this.getHeight();
10265
10266             arguments.callee.anim = this.fxanim({
10267                     width : {to: this.adjustWidth(width * 2)},
10268                     height : {to: this.adjustHeight(height * 2)},
10269                     points : {by: [-(width * .5), -(height * .5)]},
10270                     opacity : {to: 0},
10271                     fontSize: {to:200, unit: "%"}
10272                 },
10273                 o,
10274                 'motion',
10275                 .5,
10276                 "easeOut", after);
10277         });
10278         return this;
10279     },
10280
10281         /**
10282          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10283          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10284          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10285          * Usage:
10286          *<pre><code>
10287 // default
10288 el.switchOff();
10289
10290 // all config options shown with default values
10291 el.switchOff({
10292     easing: 'easeIn',
10293     duration: .3,
10294     remove: false,
10295     useDisplay: false
10296 });
10297 </code></pre>
10298          * @param {Object} options (optional) Object literal with any of the Fx config options
10299          * @return {Roo.Element} The Element
10300          */
10301     switchOff : function(o){
10302         var el = this.getFxEl();
10303         o = o || {};
10304
10305         el.queueFx(o, function(){
10306             this.clearOpacity();
10307             this.clip();
10308
10309             // restore values after effect
10310             var r = this.getFxRestore();
10311             var st = this.dom.style;
10312
10313             var after = function(){
10314                 if(o.useDisplay){
10315                     el.setDisplayed(false);
10316                 }else{
10317                     el.hide();
10318                 }
10319
10320                 el.clearOpacity();
10321                 el.setPositioning(r.pos);
10322                 st.width = r.width;
10323                 st.height = r.height;
10324
10325                 el.afterFx(o);
10326             };
10327
10328             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10329                 this.clearOpacity();
10330                 (function(){
10331                     this.fxanim({
10332                         height:{to:1},
10333                         points:{by:[0, this.getHeight() * .5]}
10334                     }, o, 'motion', 0.3, 'easeIn', after);
10335                 }).defer(100, this);
10336             });
10337         });
10338         return this;
10339     },
10340
10341     /**
10342      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10343      * changed using the "attr" config option) and then fading back to the original color. If no original
10344      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10345      * Usage:
10346 <pre><code>
10347 // default: highlight background to yellow
10348 el.highlight();
10349
10350 // custom: highlight foreground text to blue for 2 seconds
10351 el.highlight("0000ff", { attr: 'color', duration: 2 });
10352
10353 // common config options shown with default values
10354 el.highlight("ffff9c", {
10355     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10356     endColor: (current color) or "ffffff",
10357     easing: 'easeIn',
10358     duration: 1
10359 });
10360 </code></pre>
10361      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10362      * @param {Object} options (optional) Object literal with any of the Fx config options
10363      * @return {Roo.Element} The Element
10364      */ 
10365     highlight : function(color, o){
10366         var el = this.getFxEl();
10367         o = o || {};
10368
10369         el.queueFx(o, function(){
10370             color = color || "ffff9c";
10371             attr = o.attr || "backgroundColor";
10372
10373             this.clearOpacity();
10374             this.show();
10375
10376             var origColor = this.getColor(attr);
10377             var restoreColor = this.dom.style[attr];
10378             endColor = (o.endColor || origColor) || "ffffff";
10379
10380             var after = function(){
10381                 el.dom.style[attr] = restoreColor;
10382                 el.afterFx(o);
10383             };
10384
10385             var a = {};
10386             a[attr] = {from: color, to: endColor};
10387             arguments.callee.anim = this.fxanim(a,
10388                 o,
10389                 'color',
10390                 1,
10391                 'easeIn', after);
10392         });
10393         return this;
10394     },
10395
10396    /**
10397     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10398     * Usage:
10399 <pre><code>
10400 // default: a single light blue ripple
10401 el.frame();
10402
10403 // custom: 3 red ripples lasting 3 seconds total
10404 el.frame("ff0000", 3, { duration: 3 });
10405
10406 // common config options shown with default values
10407 el.frame("C3DAF9", 1, {
10408     duration: 1 //duration of entire animation (not each individual ripple)
10409     // Note: Easing is not configurable and will be ignored if included
10410 });
10411 </code></pre>
10412     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10413     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10414     * @param {Object} options (optional) Object literal with any of the Fx config options
10415     * @return {Roo.Element} The Element
10416     */
10417     frame : function(color, count, o){
10418         var el = this.getFxEl();
10419         o = o || {};
10420
10421         el.queueFx(o, function(){
10422             color = color || "#C3DAF9";
10423             if(color.length == 6){
10424                 color = "#" + color;
10425             }
10426             count = count || 1;
10427             duration = o.duration || 1;
10428             this.show();
10429
10430             var b = this.getBox();
10431             var animFn = function(){
10432                 var proxy = this.createProxy({
10433
10434                      style:{
10435                         visbility:"hidden",
10436                         position:"absolute",
10437                         "z-index":"35000", // yee haw
10438                         border:"0px solid " + color
10439                      }
10440                   });
10441                 var scale = Roo.isBorderBox ? 2 : 1;
10442                 proxy.animate({
10443                     top:{from:b.y, to:b.y - 20},
10444                     left:{from:b.x, to:b.x - 20},
10445                     borderWidth:{from:0, to:10},
10446                     opacity:{from:1, to:0},
10447                     height:{from:b.height, to:(b.height + (20*scale))},
10448                     width:{from:b.width, to:(b.width + (20*scale))}
10449                 }, duration, function(){
10450                     proxy.remove();
10451                 });
10452                 if(--count > 0){
10453                      animFn.defer((duration/2)*1000, this);
10454                 }else{
10455                     el.afterFx(o);
10456                 }
10457             };
10458             animFn.call(this);
10459         });
10460         return this;
10461     },
10462
10463    /**
10464     * Creates a pause before any subsequent queued effects begin.  If there are
10465     * no effects queued after the pause it will have no effect.
10466     * Usage:
10467 <pre><code>
10468 el.pause(1);
10469 </code></pre>
10470     * @param {Number} seconds The length of time to pause (in seconds)
10471     * @return {Roo.Element} The Element
10472     */
10473     pause : function(seconds){
10474         var el = this.getFxEl();
10475         var o = {};
10476
10477         el.queueFx(o, function(){
10478             setTimeout(function(){
10479                 el.afterFx(o);
10480             }, seconds * 1000);
10481         });
10482         return this;
10483     },
10484
10485    /**
10486     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10487     * using the "endOpacity" config option.
10488     * Usage:
10489 <pre><code>
10490 // default: fade in from opacity 0 to 100%
10491 el.fadeIn();
10492
10493 // custom: fade in from opacity 0 to 75% over 2 seconds
10494 el.fadeIn({ endOpacity: .75, duration: 2});
10495
10496 // common config options shown with default values
10497 el.fadeIn({
10498     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10499     easing: 'easeOut',
10500     duration: .5
10501 });
10502 </code></pre>
10503     * @param {Object} options (optional) Object literal with any of the Fx config options
10504     * @return {Roo.Element} The Element
10505     */
10506     fadeIn : function(o){
10507         var el = this.getFxEl();
10508         o = o || {};
10509         el.queueFx(o, function(){
10510             this.setOpacity(0);
10511             this.fixDisplay();
10512             this.dom.style.visibility = 'visible';
10513             var to = o.endOpacity || 1;
10514             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10515                 o, null, .5, "easeOut", function(){
10516                 if(to == 1){
10517                     this.clearOpacity();
10518                 }
10519                 el.afterFx(o);
10520             });
10521         });
10522         return this;
10523     },
10524
10525    /**
10526     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10527     * using the "endOpacity" config option.
10528     * Usage:
10529 <pre><code>
10530 // default: fade out from the element's current opacity to 0
10531 el.fadeOut();
10532
10533 // custom: fade out from the element's current opacity to 25% over 2 seconds
10534 el.fadeOut({ endOpacity: .25, duration: 2});
10535
10536 // common config options shown with default values
10537 el.fadeOut({
10538     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10539     easing: 'easeOut',
10540     duration: .5
10541     remove: false,
10542     useDisplay: false
10543 });
10544 </code></pre>
10545     * @param {Object} options (optional) Object literal with any of the Fx config options
10546     * @return {Roo.Element} The Element
10547     */
10548     fadeOut : function(o){
10549         var el = this.getFxEl();
10550         o = o || {};
10551         el.queueFx(o, function(){
10552             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10553                 o, null, .5, "easeOut", function(){
10554                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10555                      this.dom.style.display = "none";
10556                 }else{
10557                      this.dom.style.visibility = "hidden";
10558                 }
10559                 this.clearOpacity();
10560                 el.afterFx(o);
10561             });
10562         });
10563         return this;
10564     },
10565
10566    /**
10567     * Animates the transition of an element's dimensions from a starting height/width
10568     * to an ending height/width.
10569     * Usage:
10570 <pre><code>
10571 // change height and width to 100x100 pixels
10572 el.scale(100, 100);
10573
10574 // common config options shown with default values.  The height and width will default to
10575 // the element's existing values if passed as null.
10576 el.scale(
10577     [element's width],
10578     [element's height], {
10579     easing: 'easeOut',
10580     duration: .35
10581 });
10582 </code></pre>
10583     * @param {Number} width  The new width (pass undefined to keep the original width)
10584     * @param {Number} height  The new height (pass undefined to keep the original height)
10585     * @param {Object} options (optional) Object literal with any of the Fx config options
10586     * @return {Roo.Element} The Element
10587     */
10588     scale : function(w, h, o){
10589         this.shift(Roo.apply({}, o, {
10590             width: w,
10591             height: h
10592         }));
10593         return this;
10594     },
10595
10596    /**
10597     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10598     * Any of these properties not specified in the config object will not be changed.  This effect 
10599     * requires that at least one new dimension, position or opacity setting must be passed in on
10600     * the config object in order for the function to have any effect.
10601     * Usage:
10602 <pre><code>
10603 // slide the element horizontally to x position 200 while changing the height and opacity
10604 el.shift({ x: 200, height: 50, opacity: .8 });
10605
10606 // common config options shown with default values.
10607 el.shift({
10608     width: [element's width],
10609     height: [element's height],
10610     x: [element's x position],
10611     y: [element's y position],
10612     opacity: [element's opacity],
10613     easing: 'easeOut',
10614     duration: .35
10615 });
10616 </code></pre>
10617     * @param {Object} options  Object literal with any of the Fx config options
10618     * @return {Roo.Element} The Element
10619     */
10620     shift : function(o){
10621         var el = this.getFxEl();
10622         o = o || {};
10623         el.queueFx(o, function(){
10624             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10625             if(w !== undefined){
10626                 a.width = {to: this.adjustWidth(w)};
10627             }
10628             if(h !== undefined){
10629                 a.height = {to: this.adjustHeight(h)};
10630             }
10631             if(x !== undefined || y !== undefined){
10632                 a.points = {to: [
10633                     x !== undefined ? x : this.getX(),
10634                     y !== undefined ? y : this.getY()
10635                 ]};
10636             }
10637             if(op !== undefined){
10638                 a.opacity = {to: op};
10639             }
10640             if(o.xy !== undefined){
10641                 a.points = {to: o.xy};
10642             }
10643             arguments.callee.anim = this.fxanim(a,
10644                 o, 'motion', .35, "easeOut", function(){
10645                 el.afterFx(o);
10646             });
10647         });
10648         return this;
10649     },
10650
10651         /**
10652          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10653          * ending point of the effect.
10654          * Usage:
10655          *<pre><code>
10656 // default: slide the element downward while fading out
10657 el.ghost();
10658
10659 // custom: slide the element out to the right with a 2-second duration
10660 el.ghost('r', { duration: 2 });
10661
10662 // common config options shown with default values
10663 el.ghost('b', {
10664     easing: 'easeOut',
10665     duration: .5
10666     remove: false,
10667     useDisplay: false
10668 });
10669 </code></pre>
10670          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10671          * @param {Object} options (optional) Object literal with any of the Fx config options
10672          * @return {Roo.Element} The Element
10673          */
10674     ghost : function(anchor, o){
10675         var el = this.getFxEl();
10676         o = o || {};
10677
10678         el.queueFx(o, function(){
10679             anchor = anchor || "b";
10680
10681             // restore values after effect
10682             var r = this.getFxRestore();
10683             var w = this.getWidth(),
10684                 h = this.getHeight();
10685
10686             var st = this.dom.style;
10687
10688             var after = function(){
10689                 if(o.useDisplay){
10690                     el.setDisplayed(false);
10691                 }else{
10692                     el.hide();
10693                 }
10694
10695                 el.clearOpacity();
10696                 el.setPositioning(r.pos);
10697                 st.width = r.width;
10698                 st.height = r.height;
10699
10700                 el.afterFx(o);
10701             };
10702
10703             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10704             switch(anchor.toLowerCase()){
10705                 case "t":
10706                     pt.by = [0, -h];
10707                 break;
10708                 case "l":
10709                     pt.by = [-w, 0];
10710                 break;
10711                 case "r":
10712                     pt.by = [w, 0];
10713                 break;
10714                 case "b":
10715                     pt.by = [0, h];
10716                 break;
10717                 case "tl":
10718                     pt.by = [-w, -h];
10719                 break;
10720                 case "bl":
10721                     pt.by = [-w, h];
10722                 break;
10723                 case "br":
10724                     pt.by = [w, h];
10725                 break;
10726                 case "tr":
10727                     pt.by = [w, -h];
10728                 break;
10729             }
10730
10731             arguments.callee.anim = this.fxanim(a,
10732                 o,
10733                 'motion',
10734                 .5,
10735                 "easeOut", after);
10736         });
10737         return this;
10738     },
10739
10740         /**
10741          * Ensures that all effects queued after syncFx is called on the element are
10742          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10743          * @return {Roo.Element} The Element
10744          */
10745     syncFx : function(){
10746         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10747             block : false,
10748             concurrent : true,
10749             stopFx : false
10750         });
10751         return this;
10752     },
10753
10754         /**
10755          * Ensures that all effects queued after sequenceFx is called on the element are
10756          * run in sequence.  This is the opposite of {@link #syncFx}.
10757          * @return {Roo.Element} The Element
10758          */
10759     sequenceFx : function(){
10760         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10761             block : false,
10762             concurrent : false,
10763             stopFx : false
10764         });
10765         return this;
10766     },
10767
10768         /* @private */
10769     nextFx : function(){
10770         var ef = this.fxQueue[0];
10771         if(ef){
10772             ef.call(this);
10773         }
10774     },
10775
10776         /**
10777          * Returns true if the element has any effects actively running or queued, else returns false.
10778          * @return {Boolean} True if element has active effects, else false
10779          */
10780     hasActiveFx : function(){
10781         return this.fxQueue && this.fxQueue[0];
10782     },
10783
10784         /**
10785          * Stops any running effects and clears the element's internal effects queue if it contains
10786          * any additional effects that haven't started yet.
10787          * @return {Roo.Element} The Element
10788          */
10789     stopFx : function(){
10790         if(this.hasActiveFx()){
10791             var cur = this.fxQueue[0];
10792             if(cur && cur.anim && cur.anim.isAnimated()){
10793                 this.fxQueue = [cur]; // clear out others
10794                 cur.anim.stop(true);
10795             }
10796         }
10797         return this;
10798     },
10799
10800         /* @private */
10801     beforeFx : function(o){
10802         if(this.hasActiveFx() && !o.concurrent){
10803            if(o.stopFx){
10804                this.stopFx();
10805                return true;
10806            }
10807            return false;
10808         }
10809         return true;
10810     },
10811
10812         /**
10813          * Returns true if the element is currently blocking so that no other effect can be queued
10814          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10815          * used to ensure that an effect initiated by a user action runs to completion prior to the
10816          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10817          * @return {Boolean} True if blocking, else false
10818          */
10819     hasFxBlock : function(){
10820         var q = this.fxQueue;
10821         return q && q[0] && q[0].block;
10822     },
10823
10824         /* @private */
10825     queueFx : function(o, fn){
10826         if(!this.fxQueue){
10827             this.fxQueue = [];
10828         }
10829         if(!this.hasFxBlock()){
10830             Roo.applyIf(o, this.fxDefaults);
10831             if(!o.concurrent){
10832                 var run = this.beforeFx(o);
10833                 fn.block = o.block;
10834                 this.fxQueue.push(fn);
10835                 if(run){
10836                     this.nextFx();
10837                 }
10838             }else{
10839                 fn.call(this);
10840             }
10841         }
10842         return this;
10843     },
10844
10845         /* @private */
10846     fxWrap : function(pos, o, vis){
10847         var wrap;
10848         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10849             var wrapXY;
10850             if(o.fixPosition){
10851                 wrapXY = this.getXY();
10852             }
10853             var div = document.createElement("div");
10854             div.style.visibility = vis;
10855             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10856             wrap.setPositioning(pos);
10857             if(wrap.getStyle("position") == "static"){
10858                 wrap.position("relative");
10859             }
10860             this.clearPositioning('auto');
10861             wrap.clip();
10862             wrap.dom.appendChild(this.dom);
10863             if(wrapXY){
10864                 wrap.setXY(wrapXY);
10865             }
10866         }
10867         return wrap;
10868     },
10869
10870         /* @private */
10871     fxUnwrap : function(wrap, pos, o){
10872         this.clearPositioning();
10873         this.setPositioning(pos);
10874         if(!o.wrap){
10875             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10876             wrap.remove();
10877         }
10878     },
10879
10880         /* @private */
10881     getFxRestore : function(){
10882         var st = this.dom.style;
10883         return {pos: this.getPositioning(), width: st.width, height : st.height};
10884     },
10885
10886         /* @private */
10887     afterFx : function(o){
10888         if(o.afterStyle){
10889             this.applyStyles(o.afterStyle);
10890         }
10891         if(o.afterCls){
10892             this.addClass(o.afterCls);
10893         }
10894         if(o.remove === true){
10895             this.remove();
10896         }
10897         Roo.callback(o.callback, o.scope, [this]);
10898         if(!o.concurrent){
10899             this.fxQueue.shift();
10900             this.nextFx();
10901         }
10902     },
10903
10904         /* @private */
10905     getFxEl : function(){ // support for composite element fx
10906         return Roo.get(this.dom);
10907     },
10908
10909         /* @private */
10910     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10911         animType = animType || 'run';
10912         opt = opt || {};
10913         var anim = Roo.lib.Anim[animType](
10914             this.dom, args,
10915             (opt.duration || defaultDur) || .35,
10916             (opt.easing || defaultEase) || 'easeOut',
10917             function(){
10918                 Roo.callback(cb, this);
10919             },
10920             this
10921         );
10922         opt.anim = anim;
10923         return anim;
10924     }
10925 };
10926
10927 // backwords compat
10928 Roo.Fx.resize = Roo.Fx.scale;
10929
10930 //When included, Roo.Fx is automatically applied to Element so that all basic
10931 //effects are available directly via the Element API
10932 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10933  * Based on:
10934  * Ext JS Library 1.1.1
10935  * Copyright(c) 2006-2007, Ext JS, LLC.
10936  *
10937  * Originally Released Under LGPL - original licence link has changed is not relivant.
10938  *
10939  * Fork - LGPL
10940  * <script type="text/javascript">
10941  */
10942
10943
10944 /**
10945  * @class Roo.CompositeElement
10946  * Standard composite class. Creates a Roo.Element for every element in the collection.
10947  * <br><br>
10948  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10949  * actions will be performed on all the elements in this collection.</b>
10950  * <br><br>
10951  * All methods return <i>this</i> and can be chained.
10952  <pre><code>
10953  var els = Roo.select("#some-el div.some-class", true);
10954  // or select directly from an existing element
10955  var el = Roo.get('some-el');
10956  el.select('div.some-class', true);
10957
10958  els.setWidth(100); // all elements become 100 width
10959  els.hide(true); // all elements fade out and hide
10960  // or
10961  els.setWidth(100).hide(true);
10962  </code></pre>
10963  */
10964 Roo.CompositeElement = function(els){
10965     this.elements = [];
10966     this.addElements(els);
10967 };
10968 Roo.CompositeElement.prototype = {
10969     isComposite: true,
10970     addElements : function(els){
10971         if(!els) return this;
10972         if(typeof els == "string"){
10973             els = Roo.Element.selectorFunction(els);
10974         }
10975         var yels = this.elements;
10976         var index = yels.length-1;
10977         for(var i = 0, len = els.length; i < len; i++) {
10978                 yels[++index] = Roo.get(els[i]);
10979         }
10980         return this;
10981     },
10982
10983     /**
10984     * Clears this composite and adds the elements returned by the passed selector.
10985     * @param {String/Array} els A string CSS selector, an array of elements or an element
10986     * @return {CompositeElement} this
10987     */
10988     fill : function(els){
10989         this.elements = [];
10990         this.add(els);
10991         return this;
10992     },
10993
10994     /**
10995     * Filters this composite to only elements that match the passed selector.
10996     * @param {String} selector A string CSS selector
10997     * @param {Boolean} inverse return inverse filter (not matches)
10998     * @return {CompositeElement} this
10999     */
11000     filter : function(selector, inverse){
11001         var els = [];
11002         inverse = inverse || false;
11003         this.each(function(el){
11004             var match = inverse ? !el.is(selector) : el.is(selector);
11005             if(match){
11006                 els[els.length] = el.dom;
11007             }
11008         });
11009         this.fill(els);
11010         return this;
11011     },
11012
11013     invoke : function(fn, args){
11014         var els = this.elements;
11015         for(var i = 0, len = els.length; i < len; i++) {
11016                 Roo.Element.prototype[fn].apply(els[i], args);
11017         }
11018         return this;
11019     },
11020     /**
11021     * Adds elements to this composite.
11022     * @param {String/Array} els A string CSS selector, an array of elements or an element
11023     * @return {CompositeElement} this
11024     */
11025     add : function(els){
11026         if(typeof els == "string"){
11027             this.addElements(Roo.Element.selectorFunction(els));
11028         }else if(els.length !== undefined){
11029             this.addElements(els);
11030         }else{
11031             this.addElements([els]);
11032         }
11033         return this;
11034     },
11035     /**
11036     * Calls the passed function passing (el, this, index) for each element in this composite.
11037     * @param {Function} fn The function to call
11038     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11039     * @return {CompositeElement} this
11040     */
11041     each : function(fn, scope){
11042         var els = this.elements;
11043         for(var i = 0, len = els.length; i < len; i++){
11044             if(fn.call(scope || els[i], els[i], this, i) === false) {
11045                 break;
11046             }
11047         }
11048         return this;
11049     },
11050
11051     /**
11052      * Returns the Element object at the specified index
11053      * @param {Number} index
11054      * @return {Roo.Element}
11055      */
11056     item : function(index){
11057         return this.elements[index] || null;
11058     },
11059
11060     /**
11061      * Returns the first Element
11062      * @return {Roo.Element}
11063      */
11064     first : function(){
11065         return this.item(0);
11066     },
11067
11068     /**
11069      * Returns the last Element
11070      * @return {Roo.Element}
11071      */
11072     last : function(){
11073         return this.item(this.elements.length-1);
11074     },
11075
11076     /**
11077      * Returns the number of elements in this composite
11078      * @return Number
11079      */
11080     getCount : function(){
11081         return this.elements.length;
11082     },
11083
11084     /**
11085      * Returns true if this composite contains the passed element
11086      * @return Boolean
11087      */
11088     contains : function(el){
11089         return this.indexOf(el) !== -1;
11090     },
11091
11092     /**
11093      * Returns true if this composite contains the passed element
11094      * @return Boolean
11095      */
11096     indexOf : function(el){
11097         return this.elements.indexOf(Roo.get(el));
11098     },
11099
11100
11101     /**
11102     * Removes the specified element(s).
11103     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11104     * or an array of any of those.
11105     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11106     * @return {CompositeElement} this
11107     */
11108     removeElement : function(el, removeDom){
11109         if(el instanceof Array){
11110             for(var i = 0, len = el.length; i < len; i++){
11111                 this.removeElement(el[i]);
11112             }
11113             return this;
11114         }
11115         var index = typeof el == 'number' ? el : this.indexOf(el);
11116         if(index !== -1){
11117             if(removeDom){
11118                 var d = this.elements[index];
11119                 if(d.dom){
11120                     d.remove();
11121                 }else{
11122                     d.parentNode.removeChild(d);
11123                 }
11124             }
11125             this.elements.splice(index, 1);
11126         }
11127         return this;
11128     },
11129
11130     /**
11131     * Replaces the specified element with the passed element.
11132     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11133     * to replace.
11134     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11135     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11136     * @return {CompositeElement} this
11137     */
11138     replaceElement : function(el, replacement, domReplace){
11139         var index = typeof el == 'number' ? el : this.indexOf(el);
11140         if(index !== -1){
11141             if(domReplace){
11142                 this.elements[index].replaceWith(replacement);
11143             }else{
11144                 this.elements.splice(index, 1, Roo.get(replacement))
11145             }
11146         }
11147         return this;
11148     },
11149
11150     /**
11151      * Removes all elements.
11152      */
11153     clear : function(){
11154         this.elements = [];
11155     }
11156 };
11157 (function(){
11158     Roo.CompositeElement.createCall = function(proto, fnName){
11159         if(!proto[fnName]){
11160             proto[fnName] = function(){
11161                 return this.invoke(fnName, arguments);
11162             };
11163         }
11164     };
11165     for(var fnName in Roo.Element.prototype){
11166         if(typeof Roo.Element.prototype[fnName] == "function"){
11167             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11168         }
11169     };
11170 })();
11171 /*
11172  * Based on:
11173  * Ext JS Library 1.1.1
11174  * Copyright(c) 2006-2007, Ext JS, LLC.
11175  *
11176  * Originally Released Under LGPL - original licence link has changed is not relivant.
11177  *
11178  * Fork - LGPL
11179  * <script type="text/javascript">
11180  */
11181
11182 /**
11183  * @class Roo.CompositeElementLite
11184  * @extends Roo.CompositeElement
11185  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11186  <pre><code>
11187  var els = Roo.select("#some-el div.some-class");
11188  // or select directly from an existing element
11189  var el = Roo.get('some-el');
11190  el.select('div.some-class');
11191
11192  els.setWidth(100); // all elements become 100 width
11193  els.hide(true); // all elements fade out and hide
11194  // or
11195  els.setWidth(100).hide(true);
11196  </code></pre><br><br>
11197  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11198  * actions will be performed on all the elements in this collection.</b>
11199  */
11200 Roo.CompositeElementLite = function(els){
11201     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11202     this.el = new Roo.Element.Flyweight();
11203 };
11204 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11205     addElements : function(els){
11206         if(els){
11207             if(els instanceof Array){
11208                 this.elements = this.elements.concat(els);
11209             }else{
11210                 var yels = this.elements;
11211                 var index = yels.length-1;
11212                 for(var i = 0, len = els.length; i < len; i++) {
11213                     yels[++index] = els[i];
11214                 }
11215             }
11216         }
11217         return this;
11218     },
11219     invoke : function(fn, args){
11220         var els = this.elements;
11221         var el = this.el;
11222         for(var i = 0, len = els.length; i < len; i++) {
11223             el.dom = els[i];
11224                 Roo.Element.prototype[fn].apply(el, args);
11225         }
11226         return this;
11227     },
11228     /**
11229      * Returns a flyweight Element of the dom element object at the specified index
11230      * @param {Number} index
11231      * @return {Roo.Element}
11232      */
11233     item : function(index){
11234         if(!this.elements[index]){
11235             return null;
11236         }
11237         this.el.dom = this.elements[index];
11238         return this.el;
11239     },
11240
11241     // fixes scope with flyweight
11242     addListener : function(eventName, handler, scope, opt){
11243         var els = this.elements;
11244         for(var i = 0, len = els.length; i < len; i++) {
11245             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11246         }
11247         return this;
11248     },
11249
11250     /**
11251     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11252     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11253     * a reference to the dom node, use el.dom.</b>
11254     * @param {Function} fn The function to call
11255     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11256     * @return {CompositeElement} this
11257     */
11258     each : function(fn, scope){
11259         var els = this.elements;
11260         var el = this.el;
11261         for(var i = 0, len = els.length; i < len; i++){
11262             el.dom = els[i];
11263                 if(fn.call(scope || el, el, this, i) === false){
11264                 break;
11265             }
11266         }
11267         return this;
11268     },
11269
11270     indexOf : function(el){
11271         return this.elements.indexOf(Roo.getDom(el));
11272     },
11273
11274     replaceElement : function(el, replacement, domReplace){
11275         var index = typeof el == 'number' ? el : this.indexOf(el);
11276         if(index !== -1){
11277             replacement = Roo.getDom(replacement);
11278             if(domReplace){
11279                 var d = this.elements[index];
11280                 d.parentNode.insertBefore(replacement, d);
11281                 d.parentNode.removeChild(d);
11282             }
11283             this.elements.splice(index, 1, replacement);
11284         }
11285         return this;
11286     }
11287 });
11288 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11289
11290 /*
11291  * Based on:
11292  * Ext JS Library 1.1.1
11293  * Copyright(c) 2006-2007, Ext JS, LLC.
11294  *
11295  * Originally Released Under LGPL - original licence link has changed is not relivant.
11296  *
11297  * Fork - LGPL
11298  * <script type="text/javascript">
11299  */
11300
11301  
11302
11303 /**
11304  * @class Roo.data.Connection
11305  * @extends Roo.util.Observable
11306  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11307  * either to a configured URL, or to a URL specified at request time.<br><br>
11308  * <p>
11309  * Requests made by this class are asynchronous, and will return immediately. No data from
11310  * the server will be available to the statement immediately following the {@link #request} call.
11311  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11312  * <p>
11313  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11314  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11315  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11316  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11317  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11318  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11319  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11320  * standard DOM methods.
11321  * @constructor
11322  * @param {Object} config a configuration object.
11323  */
11324 Roo.data.Connection = function(config){
11325     Roo.apply(this, config);
11326     this.addEvents({
11327         /**
11328          * @event beforerequest
11329          * Fires before a network request is made to retrieve a data object.
11330          * @param {Connection} conn This Connection object.
11331          * @param {Object} options The options config object passed to the {@link #request} method.
11332          */
11333         "beforerequest" : true,
11334         /**
11335          * @event requestcomplete
11336          * Fires if the request was successfully completed.
11337          * @param {Connection} conn This Connection object.
11338          * @param {Object} response The XHR object containing the response data.
11339          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11340          * @param {Object} options The options config object passed to the {@link #request} method.
11341          */
11342         "requestcomplete" : true,
11343         /**
11344          * @event requestexception
11345          * Fires if an error HTTP status was returned from the server.
11346          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11347          * @param {Connection} conn This Connection object.
11348          * @param {Object} response The XHR object containing the response data.
11349          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11350          * @param {Object} options The options config object passed to the {@link #request} method.
11351          */
11352         "requestexception" : true
11353     });
11354     Roo.data.Connection.superclass.constructor.call(this);
11355 };
11356
11357 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11358     /**
11359      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11360      */
11361     /**
11362      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11363      * extra parameters to each request made by this object. (defaults to undefined)
11364      */
11365     /**
11366      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11367      *  to each request made by this object. (defaults to undefined)
11368      */
11369     /**
11370      * @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)
11371      */
11372     /**
11373      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11374      */
11375     timeout : 30000,
11376     /**
11377      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11378      * @type Boolean
11379      */
11380     autoAbort:false,
11381
11382     /**
11383      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11384      * @type Boolean
11385      */
11386     disableCaching: true,
11387
11388     /**
11389      * Sends an HTTP request to a remote server.
11390      * @param {Object} options An object which may contain the following properties:<ul>
11391      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11392      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11393      * request, a url encoded string or a function to call to get either.</li>
11394      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11395      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11396      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11397      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11398      * <li>options {Object} The parameter to the request call.</li>
11399      * <li>success {Boolean} True if the request succeeded.</li>
11400      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11401      * </ul></li>
11402      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11403      * The callback is passed the following parameters:<ul>
11404      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11405      * <li>options {Object} The parameter to the request call.</li>
11406      * </ul></li>
11407      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11408      * The callback is passed the following parameters:<ul>
11409      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11410      * <li>options {Object} The parameter to the request call.</li>
11411      * </ul></li>
11412      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11413      * for the callback function. Defaults to the browser window.</li>
11414      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11415      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11416      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11417      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11418      * params for the post data. Any params will be appended to the URL.</li>
11419      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11420      * </ul>
11421      * @return {Number} transactionId
11422      */
11423     request : function(o){
11424         if(this.fireEvent("beforerequest", this, o) !== false){
11425             var p = o.params;
11426
11427             if(typeof p == "function"){
11428                 p = p.call(o.scope||window, o);
11429             }
11430             if(typeof p == "object"){
11431                 p = Roo.urlEncode(o.params);
11432             }
11433             if(this.extraParams){
11434                 var extras = Roo.urlEncode(this.extraParams);
11435                 p = p ? (p + '&' + extras) : extras;
11436             }
11437
11438             var url = o.url || this.url;
11439             if(typeof url == 'function'){
11440                 url = url.call(o.scope||window, o);
11441             }
11442
11443             if(o.form){
11444                 var form = Roo.getDom(o.form);
11445                 url = url || form.action;
11446
11447                 var enctype = form.getAttribute("enctype");
11448                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11449                     return this.doFormUpload(o, p, url);
11450                 }
11451                 var f = Roo.lib.Ajax.serializeForm(form);
11452                 p = p ? (p + '&' + f) : f;
11453             }
11454
11455             var hs = o.headers;
11456             if(this.defaultHeaders){
11457                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11458                 if(!o.headers){
11459                     o.headers = hs;
11460                 }
11461             }
11462
11463             var cb = {
11464                 success: this.handleResponse,
11465                 failure: this.handleFailure,
11466                 scope: this,
11467                 argument: {options: o},
11468                 timeout : o.timeout || this.timeout
11469             };
11470
11471             var method = o.method||this.method||(p ? "POST" : "GET");
11472
11473             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11474                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11475             }
11476
11477             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11478                 if(o.autoAbort){
11479                     this.abort();
11480                 }
11481             }else if(this.autoAbort !== false){
11482                 this.abort();
11483             }
11484
11485             if((method == 'GET' && p) || o.xmlData){
11486                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11487                 p = '';
11488             }
11489             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11490             return this.transId;
11491         }else{
11492             Roo.callback(o.callback, o.scope, [o, null, null]);
11493             return null;
11494         }
11495     },
11496
11497     /**
11498      * Determine whether this object has a request outstanding.
11499      * @param {Number} transactionId (Optional) defaults to the last transaction
11500      * @return {Boolean} True if there is an outstanding request.
11501      */
11502     isLoading : function(transId){
11503         if(transId){
11504             return Roo.lib.Ajax.isCallInProgress(transId);
11505         }else{
11506             return this.transId ? true : false;
11507         }
11508     },
11509
11510     /**
11511      * Aborts any outstanding request.
11512      * @param {Number} transactionId (Optional) defaults to the last transaction
11513      */
11514     abort : function(transId){
11515         if(transId || this.isLoading()){
11516             Roo.lib.Ajax.abort(transId || this.transId);
11517         }
11518     },
11519
11520     // private
11521     handleResponse : function(response){
11522         this.transId = false;
11523         var options = response.argument.options;
11524         response.argument = options ? options.argument : null;
11525         this.fireEvent("requestcomplete", this, response, options);
11526         Roo.callback(options.success, options.scope, [response, options]);
11527         Roo.callback(options.callback, options.scope, [options, true, response]);
11528     },
11529
11530     // private
11531     handleFailure : function(response, e){
11532         this.transId = false;
11533         var options = response.argument.options;
11534         response.argument = options ? options.argument : null;
11535         this.fireEvent("requestexception", this, response, options, e);
11536         Roo.callback(options.failure, options.scope, [response, options]);
11537         Roo.callback(options.callback, options.scope, [options, false, response]);
11538     },
11539
11540     // private
11541     doFormUpload : function(o, ps, url){
11542         var id = Roo.id();
11543         var frame = document.createElement('iframe');
11544         frame.id = id;
11545         frame.name = id;
11546         frame.className = 'x-hidden';
11547         if(Roo.isIE){
11548             frame.src = Roo.SSL_SECURE_URL;
11549         }
11550         document.body.appendChild(frame);
11551
11552         if(Roo.isIE){
11553            document.frames[id].name = id;
11554         }
11555
11556         var form = Roo.getDom(o.form);
11557         form.target = id;
11558         form.method = 'POST';
11559         form.enctype = form.encoding = 'multipart/form-data';
11560         if(url){
11561             form.action = url;
11562         }
11563
11564         var hiddens, hd;
11565         if(ps){ // add dynamic params
11566             hiddens = [];
11567             ps = Roo.urlDecode(ps, false);
11568             for(var k in ps){
11569                 if(ps.hasOwnProperty(k)){
11570                     hd = document.createElement('input');
11571                     hd.type = 'hidden';
11572                     hd.name = k;
11573                     hd.value = ps[k];
11574                     form.appendChild(hd);
11575                     hiddens.push(hd);
11576                 }
11577             }
11578         }
11579
11580         function cb(){
11581             var r = {  // bogus response object
11582                 responseText : '',
11583                 responseXML : null
11584             };
11585
11586             r.argument = o ? o.argument : null;
11587
11588             try { //
11589                 var doc;
11590                 if(Roo.isIE){
11591                     doc = frame.contentWindow.document;
11592                 }else {
11593                     doc = (frame.contentDocument || window.frames[id].document);
11594                 }
11595                 if(doc && doc.body){
11596                     r.responseText = doc.body.innerHTML;
11597                 }
11598                 if(doc && doc.XMLDocument){
11599                     r.responseXML = doc.XMLDocument;
11600                 }else {
11601                     r.responseXML = doc;
11602                 }
11603             }
11604             catch(e) {
11605                 // ignore
11606             }
11607
11608             Roo.EventManager.removeListener(frame, 'load', cb, this);
11609
11610             this.fireEvent("requestcomplete", this, r, o);
11611             Roo.callback(o.success, o.scope, [r, o]);
11612             Roo.callback(o.callback, o.scope, [o, true, r]);
11613
11614             setTimeout(function(){document.body.removeChild(frame);}, 100);
11615         }
11616
11617         Roo.EventManager.on(frame, 'load', cb, this);
11618         form.submit();
11619
11620         if(hiddens){ // remove dynamic params
11621             for(var i = 0, len = hiddens.length; i < len; i++){
11622                 form.removeChild(hiddens[i]);
11623             }
11624         }
11625     }
11626 });
11627 /*
11628  * Based on:
11629  * Ext JS Library 1.1.1
11630  * Copyright(c) 2006-2007, Ext JS, LLC.
11631  *
11632  * Originally Released Under LGPL - original licence link has changed is not relivant.
11633  *
11634  * Fork - LGPL
11635  * <script type="text/javascript">
11636  */
11637  
11638 /**
11639  * Global Ajax request class.
11640  * 
11641  * @class Roo.Ajax
11642  * @extends Roo.data.Connection
11643  * @static
11644  * 
11645  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11646  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11647  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11648  * @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)
11649  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11650  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11651  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11652  */
11653 Roo.Ajax = new Roo.data.Connection({
11654     // fix up the docs
11655     /**
11656      * @scope Roo.Ajax
11657      * @type {Boolear} 
11658      */
11659     autoAbort : false,
11660
11661     /**
11662      * Serialize the passed form into a url encoded string
11663      * @scope Roo.Ajax
11664      * @param {String/HTMLElement} form
11665      * @return {String}
11666      */
11667     serializeForm : function(form){
11668         return Roo.lib.Ajax.serializeForm(form);
11669     }
11670 });/*
11671  * Based on:
11672  * Ext JS Library 1.1.1
11673  * Copyright(c) 2006-2007, Ext JS, LLC.
11674  *
11675  * Originally Released Under LGPL - original licence link has changed is not relivant.
11676  *
11677  * Fork - LGPL
11678  * <script type="text/javascript">
11679  */
11680
11681  
11682 /**
11683  * @class Roo.UpdateManager
11684  * @extends Roo.util.Observable
11685  * Provides AJAX-style update for Element object.<br><br>
11686  * Usage:<br>
11687  * <pre><code>
11688  * // Get it from a Roo.Element object
11689  * var el = Roo.get("foo");
11690  * var mgr = el.getUpdateManager();
11691  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11692  * ...
11693  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11694  * <br>
11695  * // or directly (returns the same UpdateManager instance)
11696  * var mgr = new Roo.UpdateManager("myElementId");
11697  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11698  * mgr.on("update", myFcnNeedsToKnow);
11699  * <br>
11700    // short handed call directly from the element object
11701    Roo.get("foo").load({
11702         url: "bar.php",
11703         scripts:true,
11704         params: "for=bar",
11705         text: "Loading Foo..."
11706    });
11707  * </code></pre>
11708  * @constructor
11709  * Create new UpdateManager directly.
11710  * @param {String/HTMLElement/Roo.Element} el The element to update
11711  * @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).
11712  */
11713 Roo.UpdateManager = function(el, forceNew){
11714     el = Roo.get(el);
11715     if(!forceNew && el.updateManager){
11716         return el.updateManager;
11717     }
11718     /**
11719      * The Element object
11720      * @type Roo.Element
11721      */
11722     this.el = el;
11723     /**
11724      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11725      * @type String
11726      */
11727     this.defaultUrl = null;
11728
11729     this.addEvents({
11730         /**
11731          * @event beforeupdate
11732          * Fired before an update is made, return false from your handler and the update is cancelled.
11733          * @param {Roo.Element} el
11734          * @param {String/Object/Function} url
11735          * @param {String/Object} params
11736          */
11737         "beforeupdate": true,
11738         /**
11739          * @event update
11740          * Fired after successful update is made.
11741          * @param {Roo.Element} el
11742          * @param {Object} oResponseObject The response Object
11743          */
11744         "update": true,
11745         /**
11746          * @event failure
11747          * Fired on update failure.
11748          * @param {Roo.Element} el
11749          * @param {Object} oResponseObject The response Object
11750          */
11751         "failure": true
11752     });
11753     var d = Roo.UpdateManager.defaults;
11754     /**
11755      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11756      * @type String
11757      */
11758     this.sslBlankUrl = d.sslBlankUrl;
11759     /**
11760      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11761      * @type Boolean
11762      */
11763     this.disableCaching = d.disableCaching;
11764     /**
11765      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11766      * @type String
11767      */
11768     this.indicatorText = d.indicatorText;
11769     /**
11770      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11771      * @type String
11772      */
11773     this.showLoadIndicator = d.showLoadIndicator;
11774     /**
11775      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11776      * @type Number
11777      */
11778     this.timeout = d.timeout;
11779
11780     /**
11781      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11782      * @type Boolean
11783      */
11784     this.loadScripts = d.loadScripts;
11785
11786     /**
11787      * Transaction object of current executing transaction
11788      */
11789     this.transaction = null;
11790
11791     /**
11792      * @private
11793      */
11794     this.autoRefreshProcId = null;
11795     /**
11796      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11797      * @type Function
11798      */
11799     this.refreshDelegate = this.refresh.createDelegate(this);
11800     /**
11801      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11802      * @type Function
11803      */
11804     this.updateDelegate = this.update.createDelegate(this);
11805     /**
11806      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11807      * @type Function
11808      */
11809     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11810     /**
11811      * @private
11812      */
11813     this.successDelegate = this.processSuccess.createDelegate(this);
11814     /**
11815      * @private
11816      */
11817     this.failureDelegate = this.processFailure.createDelegate(this);
11818
11819     if(!this.renderer){
11820      /**
11821       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11822       */
11823     this.renderer = new Roo.UpdateManager.BasicRenderer();
11824     }
11825     
11826     Roo.UpdateManager.superclass.constructor.call(this);
11827 };
11828
11829 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11830     /**
11831      * Get the Element this UpdateManager is bound to
11832      * @return {Roo.Element} The element
11833      */
11834     getEl : function(){
11835         return this.el;
11836     },
11837     /**
11838      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11839      * @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:
11840 <pre><code>
11841 um.update({<br/>
11842     url: "your-url.php",<br/>
11843     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11844     callback: yourFunction,<br/>
11845     scope: yourObject, //(optional scope)  <br/>
11846     discardUrl: false, <br/>
11847     nocache: false,<br/>
11848     text: "Loading...",<br/>
11849     timeout: 30,<br/>
11850     scripts: false<br/>
11851 });
11852 </code></pre>
11853      * The only required property is url. The optional properties nocache, text and scripts
11854      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11855      * @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}
11856      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11857      * @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.
11858      */
11859     update : function(url, params, callback, discardUrl){
11860         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11861             var method = this.method,
11862                 cfg;
11863             if(typeof url == "object"){ // must be config object
11864                 cfg = url;
11865                 url = cfg.url;
11866                 params = params || cfg.params;
11867                 callback = callback || cfg.callback;
11868                 discardUrl = discardUrl || cfg.discardUrl;
11869                 if(callback && cfg.scope){
11870                     callback = callback.createDelegate(cfg.scope);
11871                 }
11872                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11873                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11874                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11875                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11876                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11877             }
11878             this.showLoading();
11879             if(!discardUrl){
11880                 this.defaultUrl = url;
11881             }
11882             if(typeof url == "function"){
11883                 url = url.call(this);
11884             }
11885
11886             method = method || (params ? "POST" : "GET");
11887             if(method == "GET"){
11888                 url = this.prepareUrl(url);
11889             }
11890
11891             var o = Roo.apply(cfg ||{}, {
11892                 url : url,
11893                 params: params,
11894                 success: this.successDelegate,
11895                 failure: this.failureDelegate,
11896                 callback: undefined,
11897                 timeout: (this.timeout*1000),
11898                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11899             });
11900             Roo.log("updated manager called with timeout of " + o.timeout);
11901             this.transaction = Roo.Ajax.request(o);
11902         }
11903     },
11904
11905     /**
11906      * 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.
11907      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11908      * @param {String/HTMLElement} form The form Id or form element
11909      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11910      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11911      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11912      */
11913     formUpdate : function(form, url, reset, callback){
11914         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11915             if(typeof url == "function"){
11916                 url = url.call(this);
11917             }
11918             form = Roo.getDom(form);
11919             this.transaction = Roo.Ajax.request({
11920                 form: form,
11921                 url:url,
11922                 success: this.successDelegate,
11923                 failure: this.failureDelegate,
11924                 timeout: (this.timeout*1000),
11925                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11926             });
11927             this.showLoading.defer(1, this);
11928         }
11929     },
11930
11931     /**
11932      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11933      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11934      */
11935     refresh : function(callback){
11936         if(this.defaultUrl == null){
11937             return;
11938         }
11939         this.update(this.defaultUrl, null, callback, true);
11940     },
11941
11942     /**
11943      * Set this element to auto refresh.
11944      * @param {Number} interval How often to update (in seconds).
11945      * @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)
11946      * @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}
11947      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11948      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11949      */
11950     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11951         if(refreshNow){
11952             this.update(url || this.defaultUrl, params, callback, true);
11953         }
11954         if(this.autoRefreshProcId){
11955             clearInterval(this.autoRefreshProcId);
11956         }
11957         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11958     },
11959
11960     /**
11961      * Stop auto refresh on this element.
11962      */
11963      stopAutoRefresh : function(){
11964         if(this.autoRefreshProcId){
11965             clearInterval(this.autoRefreshProcId);
11966             delete this.autoRefreshProcId;
11967         }
11968     },
11969
11970     isAutoRefreshing : function(){
11971        return this.autoRefreshProcId ? true : false;
11972     },
11973     /**
11974      * Called to update the element to "Loading" state. Override to perform custom action.
11975      */
11976     showLoading : function(){
11977         if(this.showLoadIndicator){
11978             this.el.update(this.indicatorText);
11979         }
11980     },
11981
11982     /**
11983      * Adds unique parameter to query string if disableCaching = true
11984      * @private
11985      */
11986     prepareUrl : function(url){
11987         if(this.disableCaching){
11988             var append = "_dc=" + (new Date().getTime());
11989             if(url.indexOf("?") !== -1){
11990                 url += "&" + append;
11991             }else{
11992                 url += "?" + append;
11993             }
11994         }
11995         return url;
11996     },
11997
11998     /**
11999      * @private
12000      */
12001     processSuccess : function(response){
12002         this.transaction = null;
12003         if(response.argument.form && response.argument.reset){
12004             try{ // put in try/catch since some older FF releases had problems with this
12005                 response.argument.form.reset();
12006             }catch(e){}
12007         }
12008         if(this.loadScripts){
12009             this.renderer.render(this.el, response, this,
12010                 this.updateComplete.createDelegate(this, [response]));
12011         }else{
12012             this.renderer.render(this.el, response, this);
12013             this.updateComplete(response);
12014         }
12015     },
12016
12017     updateComplete : function(response){
12018         this.fireEvent("update", this.el, response);
12019         if(typeof response.argument.callback == "function"){
12020             response.argument.callback(this.el, true, response);
12021         }
12022     },
12023
12024     /**
12025      * @private
12026      */
12027     processFailure : function(response){
12028         this.transaction = null;
12029         this.fireEvent("failure", this.el, response);
12030         if(typeof response.argument.callback == "function"){
12031             response.argument.callback(this.el, false, response);
12032         }
12033     },
12034
12035     /**
12036      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12037      * @param {Object} renderer The object implementing the render() method
12038      */
12039     setRenderer : function(renderer){
12040         this.renderer = renderer;
12041     },
12042
12043     getRenderer : function(){
12044        return this.renderer;
12045     },
12046
12047     /**
12048      * Set the defaultUrl used for updates
12049      * @param {String/Function} defaultUrl The url or a function to call to get the url
12050      */
12051     setDefaultUrl : function(defaultUrl){
12052         this.defaultUrl = defaultUrl;
12053     },
12054
12055     /**
12056      * Aborts the executing transaction
12057      */
12058     abort : function(){
12059         if(this.transaction){
12060             Roo.Ajax.abort(this.transaction);
12061         }
12062     },
12063
12064     /**
12065      * Returns true if an update is in progress
12066      * @return {Boolean}
12067      */
12068     isUpdating : function(){
12069         if(this.transaction){
12070             return Roo.Ajax.isLoading(this.transaction);
12071         }
12072         return false;
12073     }
12074 });
12075
12076 /**
12077  * @class Roo.UpdateManager.defaults
12078  * @static (not really - but it helps the doc tool)
12079  * The defaults collection enables customizing the default properties of UpdateManager
12080  */
12081    Roo.UpdateManager.defaults = {
12082        /**
12083          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12084          * @type Number
12085          */
12086          timeout : 30,
12087
12088          /**
12089          * True to process scripts by default (Defaults to false).
12090          * @type Boolean
12091          */
12092         loadScripts : false,
12093
12094         /**
12095         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12096         * @type String
12097         */
12098         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12099         /**
12100          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12101          * @type Boolean
12102          */
12103         disableCaching : false,
12104         /**
12105          * Whether to show indicatorText when loading (Defaults to true).
12106          * @type Boolean
12107          */
12108         showLoadIndicator : true,
12109         /**
12110          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12111          * @type String
12112          */
12113         indicatorText : '<div class="loading-indicator">Loading...</div>'
12114    };
12115
12116 /**
12117  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12118  *Usage:
12119  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12120  * @param {String/HTMLElement/Roo.Element} el The element to update
12121  * @param {String} url The url
12122  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12123  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12124  * @static
12125  * @deprecated
12126  * @member Roo.UpdateManager
12127  */
12128 Roo.UpdateManager.updateElement = function(el, url, params, options){
12129     var um = Roo.get(el, true).getUpdateManager();
12130     Roo.apply(um, options);
12131     um.update(url, params, options ? options.callback : null);
12132 };
12133 // alias for backwards compat
12134 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12135 /**
12136  * @class Roo.UpdateManager.BasicRenderer
12137  * Default Content renderer. Updates the elements innerHTML with the responseText.
12138  */
12139 Roo.UpdateManager.BasicRenderer = function(){};
12140
12141 Roo.UpdateManager.BasicRenderer.prototype = {
12142     /**
12143      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12144      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12145      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12146      * @param {Roo.Element} el The element being rendered
12147      * @param {Object} response The YUI Connect response object
12148      * @param {UpdateManager} updateManager The calling update manager
12149      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12150      */
12151      render : function(el, response, updateManager, callback){
12152         el.update(response.responseText, updateManager.loadScripts, callback);
12153     }
12154 };
12155 /*
12156  * Based on:
12157  * Roo JS
12158  * (c)) Alan Knowles
12159  * Licence : LGPL
12160  */
12161
12162
12163 /**
12164  * @class Roo.DomTemplate
12165  * @extends Roo.Template
12166  * An effort at a dom based template engine..
12167  *
12168  * Similar to XTemplate, except it uses dom parsing to create the template..
12169  *
12170  * Supported features:
12171  *
12172  *  Tags:
12173
12174 <pre><code>
12175       {a_variable} - output encoded.
12176       {a_variable.format:("Y-m-d")} - call a method on the variable
12177       {a_variable:raw} - unencoded output
12178       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12179       {a_variable:this.method_on_template(...)} - call a method on the template object.
12180  
12181 </code></pre>
12182  *  The tpl tag:
12183 <pre><code>
12184         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12185         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12186         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12187         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12188   
12189 </code></pre>
12190  *      
12191  */
12192 Roo.DomTemplate = function()
12193 {
12194      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12195      if (this.html) {
12196         this.compile();
12197      }
12198 };
12199
12200
12201 Roo.extend(Roo.DomTemplate, Roo.Template, {
12202     /**
12203      * id counter for sub templates.
12204      */
12205     id : 0,
12206     /**
12207      * flag to indicate if dom parser is inside a pre,
12208      * it will strip whitespace if not.
12209      */
12210     inPre : false,
12211     
12212     /**
12213      * The various sub templates
12214      */
12215     tpls : false,
12216     
12217     
12218     
12219     /**
12220      *
12221      * basic tag replacing syntax
12222      * WORD:WORD()
12223      *
12224      * // you can fake an object call by doing this
12225      *  x.t:(test,tesT) 
12226      * 
12227      */
12228     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12229     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12230     
12231     iterChild : function (node, method) {
12232         
12233         var oldPre = this.inPre;
12234         if (node.tagName == 'PRE') {
12235             this.inPre = true;
12236         }
12237         for( var i = 0; i < node.childNodes.length; i++) {
12238             method.call(this, node.childNodes[i]);
12239         }
12240         this.inPre = oldPre;
12241     },
12242     
12243     
12244     
12245     /**
12246      * compile the template
12247      *
12248      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12249      *
12250      */
12251     compile: function()
12252     {
12253         var s = this.html;
12254         
12255         // covert the html into DOM...
12256         var doc = false;
12257         var div =false;
12258         try {
12259             doc = document.implementation.createHTMLDocument("");
12260             doc.documentElement.innerHTML =   this.html  ;
12261             div = doc.documentElement;
12262         } catch (e) {
12263             // old IE... - nasty -- it causes all sorts of issues.. with
12264             // images getting pulled from server..
12265             div = document.createElement('div');
12266             div.innerHTML = this.html;
12267         }
12268         //doc.documentElement.innerHTML = htmlBody
12269          
12270         
12271         
12272         this.tpls = [];
12273         var _t = this;
12274         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12275         
12276         var tpls = this.tpls;
12277         
12278         // create a top level template from the snippet..
12279         
12280         //Roo.log(div.innerHTML);
12281         
12282         var tpl = {
12283             uid : 'master',
12284             id : this.id++,
12285             attr : false,
12286             value : false,
12287             body : div.innerHTML,
12288             
12289             forCall : false,
12290             execCall : false,
12291             dom : div,
12292             isTop : true
12293             
12294         };
12295         tpls.unshift(tpl);
12296         
12297         
12298         // compile them...
12299         this.tpls = [];
12300         Roo.each(tpls, function(tp){
12301             this.compileTpl(tp);
12302             this.tpls[tp.id] = tp;
12303         }, this);
12304         
12305         this.master = tpls[0];
12306         return this;
12307         
12308         
12309     },
12310     
12311     compileNode : function(node, istop) {
12312         // test for
12313         //Roo.log(node);
12314         
12315         
12316         // skip anything not a tag..
12317         if (node.nodeType != 1) {
12318             if (node.nodeType == 3 && !this.inPre) {
12319                 // reduce white space..
12320                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12321                 
12322             }
12323             return;
12324         }
12325         
12326         var tpl = {
12327             uid : false,
12328             id : false,
12329             attr : false,
12330             value : false,
12331             body : '',
12332             
12333             forCall : false,
12334             execCall : false,
12335             dom : false,
12336             isTop : istop
12337             
12338             
12339         };
12340         
12341         
12342         switch(true) {
12343             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12344             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12345             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12346             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12347             // no default..
12348         }
12349         
12350         
12351         if (!tpl.attr) {
12352             // just itterate children..
12353             this.iterChild(node,this.compileNode);
12354             return;
12355         }
12356         tpl.uid = this.id++;
12357         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12358         node.removeAttribute('roo-'+ tpl.attr);
12359         if (tpl.attr != 'name') {
12360             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12361             node.parentNode.replaceChild(placeholder,  node);
12362         } else {
12363             
12364             var placeholder =  document.createElement('span');
12365             placeholder.className = 'roo-tpl-' + tpl.value;
12366             node.parentNode.replaceChild(placeholder,  node);
12367         }
12368         
12369         // parent now sees '{domtplXXXX}
12370         this.iterChild(node,this.compileNode);
12371         
12372         // we should now have node body...
12373         var div = document.createElement('div');
12374         div.appendChild(node);
12375         tpl.dom = node;
12376         // this has the unfortunate side effect of converting tagged attributes
12377         // eg. href="{...}" into %7C...%7D
12378         // this has been fixed by searching for those combo's although it's a bit hacky..
12379         
12380         
12381         tpl.body = div.innerHTML;
12382         
12383         
12384          
12385         tpl.id = tpl.uid;
12386         switch(tpl.attr) {
12387             case 'for' :
12388                 switch (tpl.value) {
12389                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12390                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12391                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12392                 }
12393                 break;
12394             
12395             case 'exec':
12396                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12397                 break;
12398             
12399             case 'if':     
12400                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12401                 break;
12402             
12403             case 'name':
12404                 tpl.id  = tpl.value; // replace non characters???
12405                 break;
12406             
12407         }
12408         
12409         
12410         this.tpls.push(tpl);
12411         
12412         
12413         
12414     },
12415     
12416     
12417     
12418     
12419     /**
12420      * Compile a segment of the template into a 'sub-template'
12421      *
12422      * 
12423      * 
12424      *
12425      */
12426     compileTpl : function(tpl)
12427     {
12428         var fm = Roo.util.Format;
12429         var useF = this.disableFormats !== true;
12430         
12431         var sep = Roo.isGecko ? "+\n" : ",\n";
12432         
12433         var undef = function(str) {
12434             Roo.debug && Roo.log("Property not found :"  + str);
12435             return '';
12436         };
12437           
12438         //Roo.log(tpl.body);
12439         
12440         
12441         
12442         var fn = function(m, lbrace, name, format, args)
12443         {
12444             //Roo.log("ARGS");
12445             //Roo.log(arguments);
12446             args = args ? args.replace(/\\'/g,"'") : args;
12447             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12448             if (typeof(format) == 'undefined') {
12449                 format =  'htmlEncode'; 
12450             }
12451             if (format == 'raw' ) {
12452                 format = false;
12453             }
12454             
12455             if(name.substr(0, 6) == 'domtpl'){
12456                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12457             }
12458             
12459             // build an array of options to determine if value is undefined..
12460             
12461             // basically get 'xxxx.yyyy' then do
12462             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12463             //    (function () { Roo.log("Property not found"); return ''; })() :
12464             //    ......
12465             
12466             var udef_ar = [];
12467             var lookfor = '';
12468             Roo.each(name.split('.'), function(st) {
12469                 lookfor += (lookfor.length ? '.': '') + st;
12470                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12471             });
12472             
12473             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12474             
12475             
12476             if(format && useF){
12477                 
12478                 args = args ? ',' + args : "";
12479                  
12480                 if(format.substr(0, 5) != "this."){
12481                     format = "fm." + format + '(';
12482                 }else{
12483                     format = 'this.call("'+ format.substr(5) + '", ';
12484                     args = ", values";
12485                 }
12486                 
12487                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12488             }
12489              
12490             if (args && args.length) {
12491                 // called with xxyx.yuu:(test,test)
12492                 // change to ()
12493                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12494             }
12495             // raw.. - :raw modifier..
12496             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12497             
12498         };
12499         var body;
12500         // branched to use + in gecko and [].join() in others
12501         if(Roo.isGecko){
12502             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12503                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12504                     "';};};";
12505         }else{
12506             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12507             body.push(tpl.body.replace(/(\r\n|\n)/g,
12508                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12509             body.push("'].join('');};};");
12510             body = body.join('');
12511         }
12512         
12513         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12514        
12515         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12516         eval(body);
12517         
12518         return this;
12519     },
12520      
12521     /**
12522      * same as applyTemplate, except it's done to one of the subTemplates
12523      * when using named templates, you can do:
12524      *
12525      * var str = pl.applySubTemplate('your-name', values);
12526      *
12527      * 
12528      * @param {Number} id of the template
12529      * @param {Object} values to apply to template
12530      * @param {Object} parent (normaly the instance of this object)
12531      */
12532     applySubTemplate : function(id, values, parent)
12533     {
12534         
12535         
12536         var t = this.tpls[id];
12537         
12538         
12539         try { 
12540             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12541                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12542                 return '';
12543             }
12544         } catch(e) {
12545             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12546             Roo.log(values);
12547           
12548             return '';
12549         }
12550         try { 
12551             
12552             if(t.execCall && t.execCall.call(this, values, parent)){
12553                 return '';
12554             }
12555         } catch(e) {
12556             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12557             Roo.log(values);
12558             return '';
12559         }
12560         
12561         try {
12562             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12563             parent = t.target ? values : parent;
12564             if(t.forCall && vs instanceof Array){
12565                 var buf = [];
12566                 for(var i = 0, len = vs.length; i < len; i++){
12567                     try {
12568                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12569                     } catch (e) {
12570                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12571                         Roo.log(e.body);
12572                         //Roo.log(t.compiled);
12573                         Roo.log(vs[i]);
12574                     }   
12575                 }
12576                 return buf.join('');
12577             }
12578         } catch (e) {
12579             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12580             Roo.log(values);
12581             return '';
12582         }
12583         try {
12584             return t.compiled.call(this, vs, parent);
12585         } catch (e) {
12586             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12587             Roo.log(e.body);
12588             //Roo.log(t.compiled);
12589             Roo.log(values);
12590             return '';
12591         }
12592     },
12593
12594    
12595
12596     applyTemplate : function(values){
12597         return this.master.compiled.call(this, values, {});
12598         //var s = this.subs;
12599     },
12600
12601     apply : function(){
12602         return this.applyTemplate.apply(this, arguments);
12603     }
12604
12605  });
12606
12607 Roo.DomTemplate.from = function(el){
12608     el = Roo.getDom(el);
12609     return new Roo.Domtemplate(el.value || el.innerHTML);
12610 };/*
12611  * Based on:
12612  * Ext JS Library 1.1.1
12613  * Copyright(c) 2006-2007, Ext JS, LLC.
12614  *
12615  * Originally Released Under LGPL - original licence link has changed is not relivant.
12616  *
12617  * Fork - LGPL
12618  * <script type="text/javascript">
12619  */
12620
12621 /**
12622  * @class Roo.util.DelayedTask
12623  * Provides a convenient method of performing setTimeout where a new
12624  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12625  * You can use this class to buffer
12626  * the keypress events for a certain number of milliseconds, and perform only if they stop
12627  * for that amount of time.
12628  * @constructor The parameters to this constructor serve as defaults and are not required.
12629  * @param {Function} fn (optional) The default function to timeout
12630  * @param {Object} scope (optional) The default scope of that timeout
12631  * @param {Array} args (optional) The default Array of arguments
12632  */
12633 Roo.util.DelayedTask = function(fn, scope, args){
12634     var id = null, d, t;
12635
12636     var call = function(){
12637         var now = new Date().getTime();
12638         if(now - t >= d){
12639             clearInterval(id);
12640             id = null;
12641             fn.apply(scope, args || []);
12642         }
12643     };
12644     /**
12645      * Cancels any pending timeout and queues a new one
12646      * @param {Number} delay The milliseconds to delay
12647      * @param {Function} newFn (optional) Overrides function passed to constructor
12648      * @param {Object} newScope (optional) Overrides scope passed to constructor
12649      * @param {Array} newArgs (optional) Overrides args passed to constructor
12650      */
12651     this.delay = function(delay, newFn, newScope, newArgs){
12652         if(id && delay != d){
12653             this.cancel();
12654         }
12655         d = delay;
12656         t = new Date().getTime();
12657         fn = newFn || fn;
12658         scope = newScope || scope;
12659         args = newArgs || args;
12660         if(!id){
12661             id = setInterval(call, d);
12662         }
12663     };
12664
12665     /**
12666      * Cancel the last queued timeout
12667      */
12668     this.cancel = function(){
12669         if(id){
12670             clearInterval(id);
12671             id = null;
12672         }
12673     };
12674 };/*
12675  * Based on:
12676  * Ext JS Library 1.1.1
12677  * Copyright(c) 2006-2007, Ext JS, LLC.
12678  *
12679  * Originally Released Under LGPL - original licence link has changed is not relivant.
12680  *
12681  * Fork - LGPL
12682  * <script type="text/javascript">
12683  */
12684  
12685  
12686 Roo.util.TaskRunner = function(interval){
12687     interval = interval || 10;
12688     var tasks = [], removeQueue = [];
12689     var id = 0;
12690     var running = false;
12691
12692     var stopThread = function(){
12693         running = false;
12694         clearInterval(id);
12695         id = 0;
12696     };
12697
12698     var startThread = function(){
12699         if(!running){
12700             running = true;
12701             id = setInterval(runTasks, interval);
12702         }
12703     };
12704
12705     var removeTask = function(task){
12706         removeQueue.push(task);
12707         if(task.onStop){
12708             task.onStop();
12709         }
12710     };
12711
12712     var runTasks = function(){
12713         if(removeQueue.length > 0){
12714             for(var i = 0, len = removeQueue.length; i < len; i++){
12715                 tasks.remove(removeQueue[i]);
12716             }
12717             removeQueue = [];
12718             if(tasks.length < 1){
12719                 stopThread();
12720                 return;
12721             }
12722         }
12723         var now = new Date().getTime();
12724         for(var i = 0, len = tasks.length; i < len; ++i){
12725             var t = tasks[i];
12726             var itime = now - t.taskRunTime;
12727             if(t.interval <= itime){
12728                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12729                 t.taskRunTime = now;
12730                 if(rt === false || t.taskRunCount === t.repeat){
12731                     removeTask(t);
12732                     return;
12733                 }
12734             }
12735             if(t.duration && t.duration <= (now - t.taskStartTime)){
12736                 removeTask(t);
12737             }
12738         }
12739     };
12740
12741     /**
12742      * Queues a new task.
12743      * @param {Object} task
12744      */
12745     this.start = function(task){
12746         tasks.push(task);
12747         task.taskStartTime = new Date().getTime();
12748         task.taskRunTime = 0;
12749         task.taskRunCount = 0;
12750         startThread();
12751         return task;
12752     };
12753
12754     this.stop = function(task){
12755         removeTask(task);
12756         return task;
12757     };
12758
12759     this.stopAll = function(){
12760         stopThread();
12761         for(var i = 0, len = tasks.length; i < len; i++){
12762             if(tasks[i].onStop){
12763                 tasks[i].onStop();
12764             }
12765         }
12766         tasks = [];
12767         removeQueue = [];
12768     };
12769 };
12770
12771 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12772  * Based on:
12773  * Ext JS Library 1.1.1
12774  * Copyright(c) 2006-2007, Ext JS, LLC.
12775  *
12776  * Originally Released Under LGPL - original licence link has changed is not relivant.
12777  *
12778  * Fork - LGPL
12779  * <script type="text/javascript">
12780  */
12781
12782  
12783 /**
12784  * @class Roo.util.MixedCollection
12785  * @extends Roo.util.Observable
12786  * A Collection class that maintains both numeric indexes and keys and exposes events.
12787  * @constructor
12788  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12789  * collection (defaults to false)
12790  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12791  * and return the key value for that item.  This is used when available to look up the key on items that
12792  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12793  * equivalent to providing an implementation for the {@link #getKey} method.
12794  */
12795 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12796     this.items = [];
12797     this.map = {};
12798     this.keys = [];
12799     this.length = 0;
12800     this.addEvents({
12801         /**
12802          * @event clear
12803          * Fires when the collection is cleared.
12804          */
12805         "clear" : true,
12806         /**
12807          * @event add
12808          * Fires when an item is added to the collection.
12809          * @param {Number} index The index at which the item was added.
12810          * @param {Object} o The item added.
12811          * @param {String} key The key associated with the added item.
12812          */
12813         "add" : true,
12814         /**
12815          * @event replace
12816          * Fires when an item is replaced in the collection.
12817          * @param {String} key he key associated with the new added.
12818          * @param {Object} old The item being replaced.
12819          * @param {Object} new The new item.
12820          */
12821         "replace" : true,
12822         /**
12823          * @event remove
12824          * Fires when an item is removed from the collection.
12825          * @param {Object} o The item being removed.
12826          * @param {String} key (optional) The key associated with the removed item.
12827          */
12828         "remove" : true,
12829         "sort" : true
12830     });
12831     this.allowFunctions = allowFunctions === true;
12832     if(keyFn){
12833         this.getKey = keyFn;
12834     }
12835     Roo.util.MixedCollection.superclass.constructor.call(this);
12836 };
12837
12838 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12839     allowFunctions : false,
12840     
12841 /**
12842  * Adds an item to the collection.
12843  * @param {String} key The key to associate with the item
12844  * @param {Object} o The item to add.
12845  * @return {Object} The item added.
12846  */
12847     add : function(key, o){
12848         if(arguments.length == 1){
12849             o = arguments[0];
12850             key = this.getKey(o);
12851         }
12852         if(typeof key == "undefined" || key === null){
12853             this.length++;
12854             this.items.push(o);
12855             this.keys.push(null);
12856         }else{
12857             var old = this.map[key];
12858             if(old){
12859                 return this.replace(key, o);
12860             }
12861             this.length++;
12862             this.items.push(o);
12863             this.map[key] = o;
12864             this.keys.push(key);
12865         }
12866         this.fireEvent("add", this.length-1, o, key);
12867         return o;
12868     },
12869        
12870 /**
12871   * MixedCollection has a generic way to fetch keys if you implement getKey.
12872 <pre><code>
12873 // normal way
12874 var mc = new Roo.util.MixedCollection();
12875 mc.add(someEl.dom.id, someEl);
12876 mc.add(otherEl.dom.id, otherEl);
12877 //and so on
12878
12879 // using getKey
12880 var mc = new Roo.util.MixedCollection();
12881 mc.getKey = function(el){
12882    return el.dom.id;
12883 };
12884 mc.add(someEl);
12885 mc.add(otherEl);
12886
12887 // or via the constructor
12888 var mc = new Roo.util.MixedCollection(false, function(el){
12889    return el.dom.id;
12890 });
12891 mc.add(someEl);
12892 mc.add(otherEl);
12893 </code></pre>
12894  * @param o {Object} The item for which to find the key.
12895  * @return {Object} The key for the passed item.
12896  */
12897     getKey : function(o){
12898          return o.id; 
12899     },
12900    
12901 /**
12902  * Replaces an item in the collection.
12903  * @param {String} key The key associated with the item to replace, or the item to replace.
12904  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12905  * @return {Object}  The new item.
12906  */
12907     replace : function(key, o){
12908         if(arguments.length == 1){
12909             o = arguments[0];
12910             key = this.getKey(o);
12911         }
12912         var old = this.item(key);
12913         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12914              return this.add(key, o);
12915         }
12916         var index = this.indexOfKey(key);
12917         this.items[index] = o;
12918         this.map[key] = o;
12919         this.fireEvent("replace", key, old, o);
12920         return o;
12921     },
12922    
12923 /**
12924  * Adds all elements of an Array or an Object to the collection.
12925  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12926  * an Array of values, each of which are added to the collection.
12927  */
12928     addAll : function(objs){
12929         if(arguments.length > 1 || objs instanceof Array){
12930             var args = arguments.length > 1 ? arguments : objs;
12931             for(var i = 0, len = args.length; i < len; i++){
12932                 this.add(args[i]);
12933             }
12934         }else{
12935             for(var key in objs){
12936                 if(this.allowFunctions || typeof objs[key] != "function"){
12937                     this.add(key, objs[key]);
12938                 }
12939             }
12940         }
12941     },
12942    
12943 /**
12944  * Executes the specified function once for every item in the collection, passing each
12945  * item as the first and only parameter. returning false from the function will stop the iteration.
12946  * @param {Function} fn The function to execute for each item.
12947  * @param {Object} scope (optional) The scope in which to execute the function.
12948  */
12949     each : function(fn, scope){
12950         var items = [].concat(this.items); // each safe for removal
12951         for(var i = 0, len = items.length; i < len; i++){
12952             if(fn.call(scope || items[i], items[i], i, len) === false){
12953                 break;
12954             }
12955         }
12956     },
12957    
12958 /**
12959  * Executes the specified function once for every key in the collection, passing each
12960  * key, and its associated item as the first two parameters.
12961  * @param {Function} fn The function to execute for each item.
12962  * @param {Object} scope (optional) The scope in which to execute the function.
12963  */
12964     eachKey : function(fn, scope){
12965         for(var i = 0, len = this.keys.length; i < len; i++){
12966             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12967         }
12968     },
12969    
12970 /**
12971  * Returns the first item in the collection which elicits a true return value from the
12972  * passed selection function.
12973  * @param {Function} fn The selection function to execute for each item.
12974  * @param {Object} scope (optional) The scope in which to execute the function.
12975  * @return {Object} The first item in the collection which returned true from the selection function.
12976  */
12977     find : function(fn, scope){
12978         for(var i = 0, len = this.items.length; i < len; i++){
12979             if(fn.call(scope || window, this.items[i], this.keys[i])){
12980                 return this.items[i];
12981             }
12982         }
12983         return null;
12984     },
12985    
12986 /**
12987  * Inserts an item at the specified index in the collection.
12988  * @param {Number} index The index to insert the item at.
12989  * @param {String} key The key to associate with the new item, or the item itself.
12990  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12991  * @return {Object} The item inserted.
12992  */
12993     insert : function(index, key, o){
12994         if(arguments.length == 2){
12995             o = arguments[1];
12996             key = this.getKey(o);
12997         }
12998         if(index >= this.length){
12999             return this.add(key, o);
13000         }
13001         this.length++;
13002         this.items.splice(index, 0, o);
13003         if(typeof key != "undefined" && key != null){
13004             this.map[key] = o;
13005         }
13006         this.keys.splice(index, 0, key);
13007         this.fireEvent("add", index, o, key);
13008         return o;
13009     },
13010    
13011 /**
13012  * Removed an item from the collection.
13013  * @param {Object} o The item to remove.
13014  * @return {Object} The item removed.
13015  */
13016     remove : function(o){
13017         return this.removeAt(this.indexOf(o));
13018     },
13019    
13020 /**
13021  * Remove an item from a specified index in the collection.
13022  * @param {Number} index The index within the collection of the item to remove.
13023  */
13024     removeAt : function(index){
13025         if(index < this.length && index >= 0){
13026             this.length--;
13027             var o = this.items[index];
13028             this.items.splice(index, 1);
13029             var key = this.keys[index];
13030             if(typeof key != "undefined"){
13031                 delete this.map[key];
13032             }
13033             this.keys.splice(index, 1);
13034             this.fireEvent("remove", o, key);
13035         }
13036     },
13037    
13038 /**
13039  * Removed an item associated with the passed key fom the collection.
13040  * @param {String} key The key of the item to remove.
13041  */
13042     removeKey : function(key){
13043         return this.removeAt(this.indexOfKey(key));
13044     },
13045    
13046 /**
13047  * Returns the number of items in the collection.
13048  * @return {Number} the number of items in the collection.
13049  */
13050     getCount : function(){
13051         return this.length; 
13052     },
13053    
13054 /**
13055  * Returns index within the collection of the passed Object.
13056  * @param {Object} o The item to find the index of.
13057  * @return {Number} index of the item.
13058  */
13059     indexOf : function(o){
13060         if(!this.items.indexOf){
13061             for(var i = 0, len = this.items.length; i < len; i++){
13062                 if(this.items[i] == o) return i;
13063             }
13064             return -1;
13065         }else{
13066             return this.items.indexOf(o);
13067         }
13068     },
13069    
13070 /**
13071  * Returns index within the collection of the passed key.
13072  * @param {String} key The key to find the index of.
13073  * @return {Number} index of the key.
13074  */
13075     indexOfKey : function(key){
13076         if(!this.keys.indexOf){
13077             for(var i = 0, len = this.keys.length; i < len; i++){
13078                 if(this.keys[i] == key) return i;
13079             }
13080             return -1;
13081         }else{
13082             return this.keys.indexOf(key);
13083         }
13084     },
13085    
13086 /**
13087  * Returns the item associated with the passed key OR index. Key has priority over index.
13088  * @param {String/Number} key The key or index of the item.
13089  * @return {Object} The item associated with the passed key.
13090  */
13091     item : function(key){
13092         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13093         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13094     },
13095     
13096 /**
13097  * Returns the item at the specified index.
13098  * @param {Number} index The index of the item.
13099  * @return {Object}
13100  */
13101     itemAt : function(index){
13102         return this.items[index];
13103     },
13104     
13105 /**
13106  * Returns the item associated with the passed key.
13107  * @param {String/Number} key The key of the item.
13108  * @return {Object} The item associated with the passed key.
13109  */
13110     key : function(key){
13111         return this.map[key];
13112     },
13113    
13114 /**
13115  * Returns true if the collection contains the passed Object as an item.
13116  * @param {Object} o  The Object to look for in the collection.
13117  * @return {Boolean} True if the collection contains the Object as an item.
13118  */
13119     contains : function(o){
13120         return this.indexOf(o) != -1;
13121     },
13122    
13123 /**
13124  * Returns true if the collection contains the passed Object as a key.
13125  * @param {String} key The key to look for in the collection.
13126  * @return {Boolean} True if the collection contains the Object as a key.
13127  */
13128     containsKey : function(key){
13129         return typeof this.map[key] != "undefined";
13130     },
13131    
13132 /**
13133  * Removes all items from the collection.
13134  */
13135     clear : function(){
13136         this.length = 0;
13137         this.items = [];
13138         this.keys = [];
13139         this.map = {};
13140         this.fireEvent("clear");
13141     },
13142    
13143 /**
13144  * Returns the first item in the collection.
13145  * @return {Object} the first item in the collection..
13146  */
13147     first : function(){
13148         return this.items[0]; 
13149     },
13150    
13151 /**
13152  * Returns the last item in the collection.
13153  * @return {Object} the last item in the collection..
13154  */
13155     last : function(){
13156         return this.items[this.length-1];   
13157     },
13158     
13159     _sort : function(property, dir, fn){
13160         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13161         fn = fn || function(a, b){
13162             return a-b;
13163         };
13164         var c = [], k = this.keys, items = this.items;
13165         for(var i = 0, len = items.length; i < len; i++){
13166             c[c.length] = {key: k[i], value: items[i], index: i};
13167         }
13168         c.sort(function(a, b){
13169             var v = fn(a[property], b[property]) * dsc;
13170             if(v == 0){
13171                 v = (a.index < b.index ? -1 : 1);
13172             }
13173             return v;
13174         });
13175         for(var i = 0, len = c.length; i < len; i++){
13176             items[i] = c[i].value;
13177             k[i] = c[i].key;
13178         }
13179         this.fireEvent("sort", this);
13180     },
13181     
13182     /**
13183      * Sorts this collection with the passed comparison function
13184      * @param {String} direction (optional) "ASC" or "DESC"
13185      * @param {Function} fn (optional) comparison function
13186      */
13187     sort : function(dir, fn){
13188         this._sort("value", dir, fn);
13189     },
13190     
13191     /**
13192      * Sorts this collection by keys
13193      * @param {String} direction (optional) "ASC" or "DESC"
13194      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13195      */
13196     keySort : function(dir, fn){
13197         this._sort("key", dir, fn || function(a, b){
13198             return String(a).toUpperCase()-String(b).toUpperCase();
13199         });
13200     },
13201     
13202     /**
13203      * Returns a range of items in this collection
13204      * @param {Number} startIndex (optional) defaults to 0
13205      * @param {Number} endIndex (optional) default to the last item
13206      * @return {Array} An array of items
13207      */
13208     getRange : function(start, end){
13209         var items = this.items;
13210         if(items.length < 1){
13211             return [];
13212         }
13213         start = start || 0;
13214         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13215         var r = [];
13216         if(start <= end){
13217             for(var i = start; i <= end; i++) {
13218                     r[r.length] = items[i];
13219             }
13220         }else{
13221             for(var i = start; i >= end; i--) {
13222                     r[r.length] = items[i];
13223             }
13224         }
13225         return r;
13226     },
13227         
13228     /**
13229      * Filter the <i>objects</i> in this collection by a specific property. 
13230      * Returns a new collection that has been filtered.
13231      * @param {String} property A property on your objects
13232      * @param {String/RegExp} value Either string that the property values 
13233      * should start with or a RegExp to test against the property
13234      * @return {MixedCollection} The new filtered collection
13235      */
13236     filter : function(property, value){
13237         if(!value.exec){ // not a regex
13238             value = String(value);
13239             if(value.length == 0){
13240                 return this.clone();
13241             }
13242             value = new RegExp("^" + Roo.escapeRe(value), "i");
13243         }
13244         return this.filterBy(function(o){
13245             return o && value.test(o[property]);
13246         });
13247         },
13248     
13249     /**
13250      * Filter by a function. * Returns a new collection that has been filtered.
13251      * The passed function will be called with each 
13252      * object in the collection. If the function returns true, the value is included 
13253      * otherwise it is filtered.
13254      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13255      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13256      * @return {MixedCollection} The new filtered collection
13257      */
13258     filterBy : function(fn, scope){
13259         var r = new Roo.util.MixedCollection();
13260         r.getKey = this.getKey;
13261         var k = this.keys, it = this.items;
13262         for(var i = 0, len = it.length; i < len; i++){
13263             if(fn.call(scope||this, it[i], k[i])){
13264                                 r.add(k[i], it[i]);
13265                         }
13266         }
13267         return r;
13268     },
13269     
13270     /**
13271      * Creates a duplicate of this collection
13272      * @return {MixedCollection}
13273      */
13274     clone : function(){
13275         var r = new Roo.util.MixedCollection();
13276         var k = this.keys, it = this.items;
13277         for(var i = 0, len = it.length; i < len; i++){
13278             r.add(k[i], it[i]);
13279         }
13280         r.getKey = this.getKey;
13281         return r;
13282     }
13283 });
13284 /**
13285  * Returns the item associated with the passed key or index.
13286  * @method
13287  * @param {String/Number} key The key or index of the item.
13288  * @return {Object} The item associated with the passed key.
13289  */
13290 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13291  * Based on:
13292  * Ext JS Library 1.1.1
13293  * Copyright(c) 2006-2007, Ext JS, LLC.
13294  *
13295  * Originally Released Under LGPL - original licence link has changed is not relivant.
13296  *
13297  * Fork - LGPL
13298  * <script type="text/javascript">
13299  */
13300 /**
13301  * @class Roo.util.JSON
13302  * Modified version of Douglas Crockford"s json.js that doesn"t
13303  * mess with the Object prototype 
13304  * http://www.json.org/js.html
13305  * @singleton
13306  */
13307 Roo.util.JSON = new (function(){
13308     var useHasOwn = {}.hasOwnProperty ? true : false;
13309     
13310     // crashes Safari in some instances
13311     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13312     
13313     var pad = function(n) {
13314         return n < 10 ? "0" + n : n;
13315     };
13316     
13317     var m = {
13318         "\b": '\\b',
13319         "\t": '\\t',
13320         "\n": '\\n',
13321         "\f": '\\f',
13322         "\r": '\\r',
13323         '"' : '\\"',
13324         "\\": '\\\\'
13325     };
13326
13327     var encodeString = function(s){
13328         if (/["\\\x00-\x1f]/.test(s)) {
13329             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13330                 var c = m[b];
13331                 if(c){
13332                     return c;
13333                 }
13334                 c = b.charCodeAt();
13335                 return "\\u00" +
13336                     Math.floor(c / 16).toString(16) +
13337                     (c % 16).toString(16);
13338             }) + '"';
13339         }
13340         return '"' + s + '"';
13341     };
13342     
13343     var encodeArray = function(o){
13344         var a = ["["], b, i, l = o.length, v;
13345             for (i = 0; i < l; i += 1) {
13346                 v = o[i];
13347                 switch (typeof v) {
13348                     case "undefined":
13349                     case "function":
13350                     case "unknown":
13351                         break;
13352                     default:
13353                         if (b) {
13354                             a.push(',');
13355                         }
13356                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13357                         b = true;
13358                 }
13359             }
13360             a.push("]");
13361             return a.join("");
13362     };
13363     
13364     var encodeDate = function(o){
13365         return '"' + o.getFullYear() + "-" +
13366                 pad(o.getMonth() + 1) + "-" +
13367                 pad(o.getDate()) + "T" +
13368                 pad(o.getHours()) + ":" +
13369                 pad(o.getMinutes()) + ":" +
13370                 pad(o.getSeconds()) + '"';
13371     };
13372     
13373     /**
13374      * Encodes an Object, Array or other value
13375      * @param {Mixed} o The variable to encode
13376      * @return {String} The JSON string
13377      */
13378     this.encode = function(o)
13379     {
13380         // should this be extended to fully wrap stringify..
13381         
13382         if(typeof o == "undefined" || o === null){
13383             return "null";
13384         }else if(o instanceof Array){
13385             return encodeArray(o);
13386         }else if(o instanceof Date){
13387             return encodeDate(o);
13388         }else if(typeof o == "string"){
13389             return encodeString(o);
13390         }else if(typeof o == "number"){
13391             return isFinite(o) ? String(o) : "null";
13392         }else if(typeof o == "boolean"){
13393             return String(o);
13394         }else {
13395             var a = ["{"], b, i, v;
13396             for (i in o) {
13397                 if(!useHasOwn || o.hasOwnProperty(i)) {
13398                     v = o[i];
13399                     switch (typeof v) {
13400                     case "undefined":
13401                     case "function":
13402                     case "unknown":
13403                         break;
13404                     default:
13405                         if(b){
13406                             a.push(',');
13407                         }
13408                         a.push(this.encode(i), ":",
13409                                 v === null ? "null" : this.encode(v));
13410                         b = true;
13411                     }
13412                 }
13413             }
13414             a.push("}");
13415             return a.join("");
13416         }
13417     };
13418     
13419     /**
13420      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13421      * @param {String} json The JSON string
13422      * @return {Object} The resulting object
13423      */
13424     this.decode = function(json){
13425         
13426         return  /** eval:var:json */ eval("(" + json + ')');
13427     };
13428 })();
13429 /** 
13430  * Shorthand for {@link Roo.util.JSON#encode}
13431  * @member Roo encode 
13432  * @method */
13433 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13434 /** 
13435  * Shorthand for {@link Roo.util.JSON#decode}
13436  * @member Roo decode 
13437  * @method */
13438 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13439 /*
13440  * Based on:
13441  * Ext JS Library 1.1.1
13442  * Copyright(c) 2006-2007, Ext JS, LLC.
13443  *
13444  * Originally Released Under LGPL - original licence link has changed is not relivant.
13445  *
13446  * Fork - LGPL
13447  * <script type="text/javascript">
13448  */
13449  
13450 /**
13451  * @class Roo.util.Format
13452  * Reusable data formatting functions
13453  * @singleton
13454  */
13455 Roo.util.Format = function(){
13456     var trimRe = /^\s+|\s+$/g;
13457     return {
13458         /**
13459          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13460          * @param {String} value The string to truncate
13461          * @param {Number} length The maximum length to allow before truncating
13462          * @return {String} The converted text
13463          */
13464         ellipsis : function(value, len){
13465             if(value && value.length > len){
13466                 return value.substr(0, len-3)+"...";
13467             }
13468             return value;
13469         },
13470
13471         /**
13472          * Checks a reference and converts it to empty string if it is undefined
13473          * @param {Mixed} value Reference to check
13474          * @return {Mixed} Empty string if converted, otherwise the original value
13475          */
13476         undef : function(value){
13477             return typeof value != "undefined" ? value : "";
13478         },
13479
13480         /**
13481          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13482          * @param {String} value The string to encode
13483          * @return {String} The encoded text
13484          */
13485         htmlEncode : function(value){
13486             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13487         },
13488
13489         /**
13490          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13491          * @param {String} value The string to decode
13492          * @return {String} The decoded text
13493          */
13494         htmlDecode : function(value){
13495             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13496         },
13497
13498         /**
13499          * Trims any whitespace from either side of a string
13500          * @param {String} value The text to trim
13501          * @return {String} The trimmed text
13502          */
13503         trim : function(value){
13504             return String(value).replace(trimRe, "");
13505         },
13506
13507         /**
13508          * Returns a substring from within an original string
13509          * @param {String} value The original text
13510          * @param {Number} start The start index of the substring
13511          * @param {Number} length The length of the substring
13512          * @return {String} The substring
13513          */
13514         substr : function(value, start, length){
13515             return String(value).substr(start, length);
13516         },
13517
13518         /**
13519          * Converts a string to all lower case letters
13520          * @param {String} value The text to convert
13521          * @return {String} The converted text
13522          */
13523         lowercase : function(value){
13524             return String(value).toLowerCase();
13525         },
13526
13527         /**
13528          * Converts a string to all upper case letters
13529          * @param {String} value The text to convert
13530          * @return {String} The converted text
13531          */
13532         uppercase : function(value){
13533             return String(value).toUpperCase();
13534         },
13535
13536         /**
13537          * Converts the first character only of a string to upper case
13538          * @param {String} value The text to convert
13539          * @return {String} The converted text
13540          */
13541         capitalize : function(value){
13542             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13543         },
13544
13545         // private
13546         call : function(value, fn){
13547             if(arguments.length > 2){
13548                 var args = Array.prototype.slice.call(arguments, 2);
13549                 args.unshift(value);
13550                  
13551                 return /** eval:var:value */  eval(fn).apply(window, args);
13552             }else{
13553                 /** eval:var:value */
13554                 return /** eval:var:value */ eval(fn).call(window, value);
13555             }
13556         },
13557
13558        
13559         /**
13560          * safer version of Math.toFixed..??/
13561          * @param {Number/String} value The numeric value to format
13562          * @param {Number/String} value Decimal places 
13563          * @return {String} The formatted currency string
13564          */
13565         toFixed : function(v, n)
13566         {
13567             // why not use to fixed - precision is buggered???
13568             if (!n) {
13569                 return Math.round(v-0);
13570             }
13571             var fact = Math.pow(10,n+1);
13572             v = (Math.round((v-0)*fact))/fact;
13573             var z = (''+fact).substring(2);
13574             if (v == Math.floor(v)) {
13575                 return Math.floor(v) + '.' + z;
13576             }
13577             
13578             // now just padd decimals..
13579             var ps = String(v).split('.');
13580             var fd = (ps[1] + z);
13581             var r = fd.substring(0,n); 
13582             var rm = fd.substring(n); 
13583             if (rm < 5) {
13584                 return ps[0] + '.' + r;
13585             }
13586             r*=1; // turn it into a number;
13587             r++;
13588             if (String(r).length != n) {
13589                 ps[0]*=1;
13590                 ps[0]++;
13591                 r = String(r).substring(1); // chop the end off.
13592             }
13593             
13594             return ps[0] + '.' + r;
13595              
13596         },
13597         
13598         /**
13599          * Format a number as US currency
13600          * @param {Number/String} value The numeric value to format
13601          * @return {String} The formatted currency string
13602          */
13603         usMoney : function(v){
13604             return '$' + Roo.util.Format.number(v);
13605         },
13606         
13607         /**
13608          * Format a number
13609          * eventually this should probably emulate php's number_format
13610          * @param {Number/String} value The numeric value to format
13611          * @param {Number} decimals number of decimal places
13612          * @return {String} The formatted currency string
13613          */
13614         number : function(v,decimals)
13615         {
13616             // multiply and round.
13617             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13618             var mul = Math.pow(10, decimals);
13619             var zero = String(mul).substring(1);
13620             v = (Math.round((v-0)*mul))/mul;
13621             
13622             // if it's '0' number.. then
13623             
13624             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13625             v = String(v);
13626             var ps = v.split('.');
13627             var whole = ps[0];
13628             
13629             
13630             var r = /(\d+)(\d{3})/;
13631             // add comma's
13632             while (r.test(whole)) {
13633                 whole = whole.replace(r, '$1' + ',' + '$2');
13634             }
13635             
13636             
13637             var sub = ps[1] ?
13638                     // has decimals..
13639                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13640                     // does not have decimals
13641                     (decimals ? ('.' + zero) : '');
13642             
13643             
13644             return whole + sub ;
13645         },
13646         
13647         /**
13648          * Parse a value into a formatted date using the specified format pattern.
13649          * @param {Mixed} value The value to format
13650          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13651          * @return {String} The formatted date string
13652          */
13653         date : function(v, format){
13654             if(!v){
13655                 return "";
13656             }
13657             if(!(v instanceof Date)){
13658                 v = new Date(Date.parse(v));
13659             }
13660             return v.dateFormat(format || Roo.util.Format.defaults.date);
13661         },
13662
13663         /**
13664          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13665          * @param {String} format Any valid date format string
13666          * @return {Function} The date formatting function
13667          */
13668         dateRenderer : function(format){
13669             return function(v){
13670                 return Roo.util.Format.date(v, format);  
13671             };
13672         },
13673
13674         // private
13675         stripTagsRE : /<\/?[^>]+>/gi,
13676         
13677         /**
13678          * Strips all HTML tags
13679          * @param {Mixed} value The text from which to strip tags
13680          * @return {String} The stripped text
13681          */
13682         stripTags : function(v){
13683             return !v ? v : String(v).replace(this.stripTagsRE, "");
13684         }
13685     };
13686 }();
13687 Roo.util.Format.defaults = {
13688     date : 'd/M/Y'
13689 };/*
13690  * Based on:
13691  * Ext JS Library 1.1.1
13692  * Copyright(c) 2006-2007, Ext JS, LLC.
13693  *
13694  * Originally Released Under LGPL - original licence link has changed is not relivant.
13695  *
13696  * Fork - LGPL
13697  * <script type="text/javascript">
13698  */
13699
13700
13701  
13702
13703 /**
13704  * @class Roo.MasterTemplate
13705  * @extends Roo.Template
13706  * Provides a template that can have child templates. The syntax is:
13707 <pre><code>
13708 var t = new Roo.MasterTemplate(
13709         '&lt;select name="{name}"&gt;',
13710                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13711         '&lt;/select&gt;'
13712 );
13713 t.add('options', {value: 'foo', text: 'bar'});
13714 // or you can add multiple child elements in one shot
13715 t.addAll('options', [
13716     {value: 'foo', text: 'bar'},
13717     {value: 'foo2', text: 'bar2'},
13718     {value: 'foo3', text: 'bar3'}
13719 ]);
13720 // then append, applying the master template values
13721 t.append('my-form', {name: 'my-select'});
13722 </code></pre>
13723 * A name attribute for the child template is not required if you have only one child
13724 * template or you want to refer to them by index.
13725  */
13726 Roo.MasterTemplate = function(){
13727     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13728     this.originalHtml = this.html;
13729     var st = {};
13730     var m, re = this.subTemplateRe;
13731     re.lastIndex = 0;
13732     var subIndex = 0;
13733     while(m = re.exec(this.html)){
13734         var name = m[1], content = m[2];
13735         st[subIndex] = {
13736             name: name,
13737             index: subIndex,
13738             buffer: [],
13739             tpl : new Roo.Template(content)
13740         };
13741         if(name){
13742             st[name] = st[subIndex];
13743         }
13744         st[subIndex].tpl.compile();
13745         st[subIndex].tpl.call = this.call.createDelegate(this);
13746         subIndex++;
13747     }
13748     this.subCount = subIndex;
13749     this.subs = st;
13750 };
13751 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13752     /**
13753     * The regular expression used to match sub templates
13754     * @type RegExp
13755     * @property
13756     */
13757     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13758
13759     /**
13760      * Applies the passed values to a child template.
13761      * @param {String/Number} name (optional) The name or index of the child template
13762      * @param {Array/Object} values The values to be applied to the template
13763      * @return {MasterTemplate} this
13764      */
13765      add : function(name, values){
13766         if(arguments.length == 1){
13767             values = arguments[0];
13768             name = 0;
13769         }
13770         var s = this.subs[name];
13771         s.buffer[s.buffer.length] = s.tpl.apply(values);
13772         return this;
13773     },
13774
13775     /**
13776      * Applies all the passed values to a child template.
13777      * @param {String/Number} name (optional) The name or index of the child template
13778      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13779      * @param {Boolean} reset (optional) True to reset the template first
13780      * @return {MasterTemplate} this
13781      */
13782     fill : function(name, values, reset){
13783         var a = arguments;
13784         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13785             values = a[0];
13786             name = 0;
13787             reset = a[1];
13788         }
13789         if(reset){
13790             this.reset();
13791         }
13792         for(var i = 0, len = values.length; i < len; i++){
13793             this.add(name, values[i]);
13794         }
13795         return this;
13796     },
13797
13798     /**
13799      * Resets the template for reuse
13800      * @return {MasterTemplate} this
13801      */
13802      reset : function(){
13803         var s = this.subs;
13804         for(var i = 0; i < this.subCount; i++){
13805             s[i].buffer = [];
13806         }
13807         return this;
13808     },
13809
13810     applyTemplate : function(values){
13811         var s = this.subs;
13812         var replaceIndex = -1;
13813         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13814             return s[++replaceIndex].buffer.join("");
13815         });
13816         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13817     },
13818
13819     apply : function(){
13820         return this.applyTemplate.apply(this, arguments);
13821     },
13822
13823     compile : function(){return this;}
13824 });
13825
13826 /**
13827  * Alias for fill().
13828  * @method
13829  */
13830 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13831  /**
13832  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13833  * var tpl = Roo.MasterTemplate.from('element-id');
13834  * @param {String/HTMLElement} el
13835  * @param {Object} config
13836  * @static
13837  */
13838 Roo.MasterTemplate.from = function(el, config){
13839     el = Roo.getDom(el);
13840     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13841 };/*
13842  * Based on:
13843  * Ext JS Library 1.1.1
13844  * Copyright(c) 2006-2007, Ext JS, LLC.
13845  *
13846  * Originally Released Under LGPL - original licence link has changed is not relivant.
13847  *
13848  * Fork - LGPL
13849  * <script type="text/javascript">
13850  */
13851
13852  
13853 /**
13854  * @class Roo.util.CSS
13855  * Utility class for manipulating CSS rules
13856  * @singleton
13857  */
13858 Roo.util.CSS = function(){
13859         var rules = null;
13860         var doc = document;
13861
13862     var camelRe = /(-[a-z])/gi;
13863     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13864
13865    return {
13866    /**
13867     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13868     * tag and appended to the HEAD of the document.
13869     * @param {String|Object} cssText The text containing the css rules
13870     * @param {String} id An id to add to the stylesheet for later removal
13871     * @return {StyleSheet}
13872     */
13873     createStyleSheet : function(cssText, id){
13874         var ss;
13875         var head = doc.getElementsByTagName("head")[0];
13876         var nrules = doc.createElement("style");
13877         nrules.setAttribute("type", "text/css");
13878         if(id){
13879             nrules.setAttribute("id", id);
13880         }
13881         if (typeof(cssText) != 'string') {
13882             // support object maps..
13883             // not sure if this a good idea.. 
13884             // perhaps it should be merged with the general css handling
13885             // and handle js style props.
13886             var cssTextNew = [];
13887             for(var n in cssText) {
13888                 var citems = [];
13889                 for(var k in cssText[n]) {
13890                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13891                 }
13892                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13893                 
13894             }
13895             cssText = cssTextNew.join("\n");
13896             
13897         }
13898        
13899        
13900        if(Roo.isIE){
13901            head.appendChild(nrules);
13902            ss = nrules.styleSheet;
13903            ss.cssText = cssText;
13904        }else{
13905            try{
13906                 nrules.appendChild(doc.createTextNode(cssText));
13907            }catch(e){
13908                nrules.cssText = cssText; 
13909            }
13910            head.appendChild(nrules);
13911            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13912        }
13913        this.cacheStyleSheet(ss);
13914        return ss;
13915    },
13916
13917    /**
13918     * Removes a style or link tag by id
13919     * @param {String} id The id of the tag
13920     */
13921    removeStyleSheet : function(id){
13922        var existing = doc.getElementById(id);
13923        if(existing){
13924            existing.parentNode.removeChild(existing);
13925        }
13926    },
13927
13928    /**
13929     * Dynamically swaps an existing stylesheet reference for a new one
13930     * @param {String} id The id of an existing link tag to remove
13931     * @param {String} url The href of the new stylesheet to include
13932     */
13933    swapStyleSheet : function(id, url){
13934        this.removeStyleSheet(id);
13935        var ss = doc.createElement("link");
13936        ss.setAttribute("rel", "stylesheet");
13937        ss.setAttribute("type", "text/css");
13938        ss.setAttribute("id", id);
13939        ss.setAttribute("href", url);
13940        doc.getElementsByTagName("head")[0].appendChild(ss);
13941    },
13942    
13943    /**
13944     * Refresh the rule cache if you have dynamically added stylesheets
13945     * @return {Object} An object (hash) of rules indexed by selector
13946     */
13947    refreshCache : function(){
13948        return this.getRules(true);
13949    },
13950
13951    // private
13952    cacheStyleSheet : function(stylesheet){
13953        if(!rules){
13954            rules = {};
13955        }
13956        try{// try catch for cross domain access issue
13957            var ssRules = stylesheet.cssRules || stylesheet.rules;
13958            for(var j = ssRules.length-1; j >= 0; --j){
13959                rules[ssRules[j].selectorText] = ssRules[j];
13960            }
13961        }catch(e){}
13962    },
13963    
13964    /**
13965     * Gets all css rules for the document
13966     * @param {Boolean} refreshCache true to refresh the internal cache
13967     * @return {Object} An object (hash) of rules indexed by selector
13968     */
13969    getRules : function(refreshCache){
13970                 if(rules == null || refreshCache){
13971                         rules = {};
13972                         var ds = doc.styleSheets;
13973                         for(var i =0, len = ds.length; i < len; i++){
13974                             try{
13975                         this.cacheStyleSheet(ds[i]);
13976                     }catch(e){} 
13977                 }
13978                 }
13979                 return rules;
13980         },
13981         
13982         /**
13983     * Gets an an individual CSS rule by selector(s)
13984     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13985     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13986     * @return {CSSRule} The CSS rule or null if one is not found
13987     */
13988    getRule : function(selector, refreshCache){
13989                 var rs = this.getRules(refreshCache);
13990                 if(!(selector instanceof Array)){
13991                     return rs[selector];
13992                 }
13993                 for(var i = 0; i < selector.length; i++){
13994                         if(rs[selector[i]]){
13995                                 return rs[selector[i]];
13996                         }
13997                 }
13998                 return null;
13999         },
14000         
14001         
14002         /**
14003     * Updates a rule property
14004     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14005     * @param {String} property The css property
14006     * @param {String} value The new value for the property
14007     * @return {Boolean} true If a rule was found and updated
14008     */
14009    updateRule : function(selector, property, value){
14010                 if(!(selector instanceof Array)){
14011                         var rule = this.getRule(selector);
14012                         if(rule){
14013                                 rule.style[property.replace(camelRe, camelFn)] = value;
14014                                 return true;
14015                         }
14016                 }else{
14017                         for(var i = 0; i < selector.length; i++){
14018                                 if(this.updateRule(selector[i], property, value)){
14019                                         return true;
14020                                 }
14021                         }
14022                 }
14023                 return false;
14024         }
14025    };   
14026 }();/*
14027  * Based on:
14028  * Ext JS Library 1.1.1
14029  * Copyright(c) 2006-2007, Ext JS, LLC.
14030  *
14031  * Originally Released Under LGPL - original licence link has changed is not relivant.
14032  *
14033  * Fork - LGPL
14034  * <script type="text/javascript">
14035  */
14036
14037  
14038
14039 /**
14040  * @class Roo.util.ClickRepeater
14041  * @extends Roo.util.Observable
14042  * 
14043  * A wrapper class which can be applied to any element. Fires a "click" event while the
14044  * mouse is pressed. The interval between firings may be specified in the config but
14045  * defaults to 10 milliseconds.
14046  * 
14047  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14048  * 
14049  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14050  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14051  * Similar to an autorepeat key delay.
14052  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14053  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14054  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14055  *           "interval" and "delay" are ignored. "immediate" is honored.
14056  * @cfg {Boolean} preventDefault True to prevent the default click event
14057  * @cfg {Boolean} stopDefault True to stop the default click event
14058  * 
14059  * @history
14060  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14061  *     2007-02-02 jvs Renamed to ClickRepeater
14062  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14063  *
14064  *  @constructor
14065  * @param {String/HTMLElement/Element} el The element to listen on
14066  * @param {Object} config
14067  **/
14068 Roo.util.ClickRepeater = function(el, config)
14069 {
14070     this.el = Roo.get(el);
14071     this.el.unselectable();
14072
14073     Roo.apply(this, config);
14074
14075     this.addEvents({
14076     /**
14077      * @event mousedown
14078      * Fires when the mouse button is depressed.
14079      * @param {Roo.util.ClickRepeater} this
14080      */
14081         "mousedown" : true,
14082     /**
14083      * @event click
14084      * Fires on a specified interval during the time the element is pressed.
14085      * @param {Roo.util.ClickRepeater} this
14086      */
14087         "click" : true,
14088     /**
14089      * @event mouseup
14090      * Fires when the mouse key is released.
14091      * @param {Roo.util.ClickRepeater} this
14092      */
14093         "mouseup" : true
14094     });
14095
14096     this.el.on("mousedown", this.handleMouseDown, this);
14097     if(this.preventDefault || this.stopDefault){
14098         this.el.on("click", function(e){
14099             if(this.preventDefault){
14100                 e.preventDefault();
14101             }
14102             if(this.stopDefault){
14103                 e.stopEvent();
14104             }
14105         }, this);
14106     }
14107
14108     // allow inline handler
14109     if(this.handler){
14110         this.on("click", this.handler,  this.scope || this);
14111     }
14112
14113     Roo.util.ClickRepeater.superclass.constructor.call(this);
14114 };
14115
14116 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14117     interval : 20,
14118     delay: 250,
14119     preventDefault : true,
14120     stopDefault : false,
14121     timer : 0,
14122
14123     // private
14124     handleMouseDown : function(){
14125         clearTimeout(this.timer);
14126         this.el.blur();
14127         if(this.pressClass){
14128             this.el.addClass(this.pressClass);
14129         }
14130         this.mousedownTime = new Date();
14131
14132         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14133         this.el.on("mouseout", this.handleMouseOut, this);
14134
14135         this.fireEvent("mousedown", this);
14136         this.fireEvent("click", this);
14137         
14138         this.timer = this.click.defer(this.delay || this.interval, this);
14139     },
14140
14141     // private
14142     click : function(){
14143         this.fireEvent("click", this);
14144         this.timer = this.click.defer(this.getInterval(), this);
14145     },
14146
14147     // private
14148     getInterval: function(){
14149         if(!this.accelerate){
14150             return this.interval;
14151         }
14152         var pressTime = this.mousedownTime.getElapsed();
14153         if(pressTime < 500){
14154             return 400;
14155         }else if(pressTime < 1700){
14156             return 320;
14157         }else if(pressTime < 2600){
14158             return 250;
14159         }else if(pressTime < 3500){
14160             return 180;
14161         }else if(pressTime < 4400){
14162             return 140;
14163         }else if(pressTime < 5300){
14164             return 80;
14165         }else if(pressTime < 6200){
14166             return 50;
14167         }else{
14168             return 10;
14169         }
14170     },
14171
14172     // private
14173     handleMouseOut : function(){
14174         clearTimeout(this.timer);
14175         if(this.pressClass){
14176             this.el.removeClass(this.pressClass);
14177         }
14178         this.el.on("mouseover", this.handleMouseReturn, this);
14179     },
14180
14181     // private
14182     handleMouseReturn : function(){
14183         this.el.un("mouseover", this.handleMouseReturn);
14184         if(this.pressClass){
14185             this.el.addClass(this.pressClass);
14186         }
14187         this.click();
14188     },
14189
14190     // private
14191     handleMouseUp : function(){
14192         clearTimeout(this.timer);
14193         this.el.un("mouseover", this.handleMouseReturn);
14194         this.el.un("mouseout", this.handleMouseOut);
14195         Roo.get(document).un("mouseup", this.handleMouseUp);
14196         this.el.removeClass(this.pressClass);
14197         this.fireEvent("mouseup", this);
14198     }
14199 });/*
14200  * Based on:
14201  * Ext JS Library 1.1.1
14202  * Copyright(c) 2006-2007, Ext JS, LLC.
14203  *
14204  * Originally Released Under LGPL - original licence link has changed is not relivant.
14205  *
14206  * Fork - LGPL
14207  * <script type="text/javascript">
14208  */
14209
14210  
14211 /**
14212  * @class Roo.KeyNav
14213  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14214  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14215  * way to implement custom navigation schemes for any UI component.</p>
14216  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14217  * pageUp, pageDown, del, home, end.  Usage:</p>
14218  <pre><code>
14219 var nav = new Roo.KeyNav("my-element", {
14220     "left" : function(e){
14221         this.moveLeft(e.ctrlKey);
14222     },
14223     "right" : function(e){
14224         this.moveRight(e.ctrlKey);
14225     },
14226     "enter" : function(e){
14227         this.save();
14228     },
14229     scope : this
14230 });
14231 </code></pre>
14232  * @constructor
14233  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14234  * @param {Object} config The config
14235  */
14236 Roo.KeyNav = function(el, config){
14237     this.el = Roo.get(el);
14238     Roo.apply(this, config);
14239     if(!this.disabled){
14240         this.disabled = true;
14241         this.enable();
14242     }
14243 };
14244
14245 Roo.KeyNav.prototype = {
14246     /**
14247      * @cfg {Boolean} disabled
14248      * True to disable this KeyNav instance (defaults to false)
14249      */
14250     disabled : false,
14251     /**
14252      * @cfg {String} defaultEventAction
14253      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14254      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14255      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14256      */
14257     defaultEventAction: "stopEvent",
14258     /**
14259      * @cfg {Boolean} forceKeyDown
14260      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14261      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14262      * handle keydown instead of keypress.
14263      */
14264     forceKeyDown : false,
14265
14266     // private
14267     prepareEvent : function(e){
14268         var k = e.getKey();
14269         var h = this.keyToHandler[k];
14270         //if(h && this[h]){
14271         //    e.stopPropagation();
14272         //}
14273         if(Roo.isSafari && h && k >= 37 && k <= 40){
14274             e.stopEvent();
14275         }
14276     },
14277
14278     // private
14279     relay : function(e){
14280         var k = e.getKey();
14281         var h = this.keyToHandler[k];
14282         if(h && this[h]){
14283             if(this.doRelay(e, this[h], h) !== true){
14284                 e[this.defaultEventAction]();
14285             }
14286         }
14287     },
14288
14289     // private
14290     doRelay : function(e, h, hname){
14291         return h.call(this.scope || this, e);
14292     },
14293
14294     // possible handlers
14295     enter : false,
14296     left : false,
14297     right : false,
14298     up : false,
14299     down : false,
14300     tab : false,
14301     esc : false,
14302     pageUp : false,
14303     pageDown : false,
14304     del : false,
14305     home : false,
14306     end : false,
14307
14308     // quick lookup hash
14309     keyToHandler : {
14310         37 : "left",
14311         39 : "right",
14312         38 : "up",
14313         40 : "down",
14314         33 : "pageUp",
14315         34 : "pageDown",
14316         46 : "del",
14317         36 : "home",
14318         35 : "end",
14319         13 : "enter",
14320         27 : "esc",
14321         9  : "tab"
14322     },
14323
14324         /**
14325          * Enable this KeyNav
14326          */
14327         enable: function(){
14328                 if(this.disabled){
14329             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14330             // the EventObject will normalize Safari automatically
14331             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14332                 this.el.on("keydown", this.relay,  this);
14333             }else{
14334                 this.el.on("keydown", this.prepareEvent,  this);
14335                 this.el.on("keypress", this.relay,  this);
14336             }
14337                     this.disabled = false;
14338                 }
14339         },
14340
14341         /**
14342          * Disable this KeyNav
14343          */
14344         disable: function(){
14345                 if(!this.disabled){
14346                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14347                 this.el.un("keydown", this.relay);
14348             }else{
14349                 this.el.un("keydown", this.prepareEvent);
14350                 this.el.un("keypress", this.relay);
14351             }
14352                     this.disabled = true;
14353                 }
14354         }
14355 };/*
14356  * Based on:
14357  * Ext JS Library 1.1.1
14358  * Copyright(c) 2006-2007, Ext JS, LLC.
14359  *
14360  * Originally Released Under LGPL - original licence link has changed is not relivant.
14361  *
14362  * Fork - LGPL
14363  * <script type="text/javascript">
14364  */
14365
14366  
14367 /**
14368  * @class Roo.KeyMap
14369  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14370  * The constructor accepts the same config object as defined by {@link #addBinding}.
14371  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14372  * combination it will call the function with this signature (if the match is a multi-key
14373  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14374  * A KeyMap can also handle a string representation of keys.<br />
14375  * Usage:
14376  <pre><code>
14377 // map one key by key code
14378 var map = new Roo.KeyMap("my-element", {
14379     key: 13, // or Roo.EventObject.ENTER
14380     fn: myHandler,
14381     scope: myObject
14382 });
14383
14384 // map multiple keys to one action by string
14385 var map = new Roo.KeyMap("my-element", {
14386     key: "a\r\n\t",
14387     fn: myHandler,
14388     scope: myObject
14389 });
14390
14391 // map multiple keys to multiple actions by strings and array of codes
14392 var map = new Roo.KeyMap("my-element", [
14393     {
14394         key: [10,13],
14395         fn: function(){ alert("Return was pressed"); }
14396     }, {
14397         key: "abc",
14398         fn: function(){ alert('a, b or c was pressed'); }
14399     }, {
14400         key: "\t",
14401         ctrl:true,
14402         shift:true,
14403         fn: function(){ alert('Control + shift + tab was pressed.'); }
14404     }
14405 ]);
14406 </code></pre>
14407  * <b>Note: A KeyMap starts enabled</b>
14408  * @constructor
14409  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14410  * @param {Object} config The config (see {@link #addBinding})
14411  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14412  */
14413 Roo.KeyMap = function(el, config, eventName){
14414     this.el  = Roo.get(el);
14415     this.eventName = eventName || "keydown";
14416     this.bindings = [];
14417     if(config){
14418         this.addBinding(config);
14419     }
14420     this.enable();
14421 };
14422
14423 Roo.KeyMap.prototype = {
14424     /**
14425      * True to stop the event from bubbling and prevent the default browser action if the
14426      * key was handled by the KeyMap (defaults to false)
14427      * @type Boolean
14428      */
14429     stopEvent : false,
14430
14431     /**
14432      * Add a new binding to this KeyMap. The following config object properties are supported:
14433      * <pre>
14434 Property    Type             Description
14435 ----------  ---------------  ----------------------------------------------------------------------
14436 key         String/Array     A single keycode or an array of keycodes to handle
14437 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14438 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14439 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14440 fn          Function         The function to call when KeyMap finds the expected key combination
14441 scope       Object           The scope of the callback function
14442 </pre>
14443      *
14444      * Usage:
14445      * <pre><code>
14446 // Create a KeyMap
14447 var map = new Roo.KeyMap(document, {
14448     key: Roo.EventObject.ENTER,
14449     fn: handleKey,
14450     scope: this
14451 });
14452
14453 //Add a new binding to the existing KeyMap later
14454 map.addBinding({
14455     key: 'abc',
14456     shift: true,
14457     fn: handleKey,
14458     scope: this
14459 });
14460 </code></pre>
14461      * @param {Object/Array} config A single KeyMap config or an array of configs
14462      */
14463         addBinding : function(config){
14464         if(config instanceof Array){
14465             for(var i = 0, len = config.length; i < len; i++){
14466                 this.addBinding(config[i]);
14467             }
14468             return;
14469         }
14470         var keyCode = config.key,
14471             shift = config.shift, 
14472             ctrl = config.ctrl, 
14473             alt = config.alt,
14474             fn = config.fn,
14475             scope = config.scope;
14476         if(typeof keyCode == "string"){
14477             var ks = [];
14478             var keyString = keyCode.toUpperCase();
14479             for(var j = 0, len = keyString.length; j < len; j++){
14480                 ks.push(keyString.charCodeAt(j));
14481             }
14482             keyCode = ks;
14483         }
14484         var keyArray = keyCode instanceof Array;
14485         var handler = function(e){
14486             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14487                 var k = e.getKey();
14488                 if(keyArray){
14489                     for(var i = 0, len = keyCode.length; i < len; i++){
14490                         if(keyCode[i] == k){
14491                           if(this.stopEvent){
14492                               e.stopEvent();
14493                           }
14494                           fn.call(scope || window, k, e);
14495                           return;
14496                         }
14497                     }
14498                 }else{
14499                     if(k == keyCode){
14500                         if(this.stopEvent){
14501                            e.stopEvent();
14502                         }
14503                         fn.call(scope || window, k, e);
14504                     }
14505                 }
14506             }
14507         };
14508         this.bindings.push(handler);  
14509         },
14510
14511     /**
14512      * Shorthand for adding a single key listener
14513      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14514      * following options:
14515      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14516      * @param {Function} fn The function to call
14517      * @param {Object} scope (optional) The scope of the function
14518      */
14519     on : function(key, fn, scope){
14520         var keyCode, shift, ctrl, alt;
14521         if(typeof key == "object" && !(key instanceof Array)){
14522             keyCode = key.key;
14523             shift = key.shift;
14524             ctrl = key.ctrl;
14525             alt = key.alt;
14526         }else{
14527             keyCode = key;
14528         }
14529         this.addBinding({
14530             key: keyCode,
14531             shift: shift,
14532             ctrl: ctrl,
14533             alt: alt,
14534             fn: fn,
14535             scope: scope
14536         })
14537     },
14538
14539     // private
14540     handleKeyDown : function(e){
14541             if(this.enabled){ //just in case
14542             var b = this.bindings;
14543             for(var i = 0, len = b.length; i < len; i++){
14544                 b[i].call(this, e);
14545             }
14546             }
14547         },
14548         
14549         /**
14550          * Returns true if this KeyMap is enabled
14551          * @return {Boolean} 
14552          */
14553         isEnabled : function(){
14554             return this.enabled;  
14555         },
14556         
14557         /**
14558          * Enables this KeyMap
14559          */
14560         enable: function(){
14561                 if(!this.enabled){
14562                     this.el.on(this.eventName, this.handleKeyDown, this);
14563                     this.enabled = true;
14564                 }
14565         },
14566
14567         /**
14568          * Disable this KeyMap
14569          */
14570         disable: function(){
14571                 if(this.enabled){
14572                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14573                     this.enabled = false;
14574                 }
14575         }
14576 };/*
14577  * Based on:
14578  * Ext JS Library 1.1.1
14579  * Copyright(c) 2006-2007, Ext JS, LLC.
14580  *
14581  * Originally Released Under LGPL - original licence link has changed is not relivant.
14582  *
14583  * Fork - LGPL
14584  * <script type="text/javascript">
14585  */
14586
14587  
14588 /**
14589  * @class Roo.util.TextMetrics
14590  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14591  * wide, in pixels, a given block of text will be.
14592  * @singleton
14593  */
14594 Roo.util.TextMetrics = function(){
14595     var shared;
14596     return {
14597         /**
14598          * Measures the size of the specified text
14599          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14600          * that can affect the size of the rendered text
14601          * @param {String} text The text to measure
14602          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14603          * in order to accurately measure the text height
14604          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14605          */
14606         measure : function(el, text, fixedWidth){
14607             if(!shared){
14608                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14609             }
14610             shared.bind(el);
14611             shared.setFixedWidth(fixedWidth || 'auto');
14612             return shared.getSize(text);
14613         },
14614
14615         /**
14616          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14617          * the overhead of multiple calls to initialize the style properties on each measurement.
14618          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14619          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14620          * in order to accurately measure the text height
14621          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14622          */
14623         createInstance : function(el, fixedWidth){
14624             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14625         }
14626     };
14627 }();
14628
14629  
14630
14631 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14632     var ml = new Roo.Element(document.createElement('div'));
14633     document.body.appendChild(ml.dom);
14634     ml.position('absolute');
14635     ml.setLeftTop(-1000, -1000);
14636     ml.hide();
14637
14638     if(fixedWidth){
14639         ml.setWidth(fixedWidth);
14640     }
14641      
14642     var instance = {
14643         /**
14644          * Returns the size of the specified text based on the internal element's style and width properties
14645          * @memberOf Roo.util.TextMetrics.Instance#
14646          * @param {String} text The text to measure
14647          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14648          */
14649         getSize : function(text){
14650             ml.update(text);
14651             var s = ml.getSize();
14652             ml.update('');
14653             return s;
14654         },
14655
14656         /**
14657          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14658          * that can affect the size of the rendered text
14659          * @memberOf Roo.util.TextMetrics.Instance#
14660          * @param {String/HTMLElement} el The element, dom node or id
14661          */
14662         bind : function(el){
14663             ml.setStyle(
14664                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14665             );
14666         },
14667
14668         /**
14669          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14670          * to set a fixed width in order to accurately measure the text height.
14671          * @memberOf Roo.util.TextMetrics.Instance#
14672          * @param {Number} width The width to set on the element
14673          */
14674         setFixedWidth : function(width){
14675             ml.setWidth(width);
14676         },
14677
14678         /**
14679          * Returns the measured width of the specified text
14680          * @memberOf Roo.util.TextMetrics.Instance#
14681          * @param {String} text The text to measure
14682          * @return {Number} width The width in pixels
14683          */
14684         getWidth : function(text){
14685             ml.dom.style.width = 'auto';
14686             return this.getSize(text).width;
14687         },
14688
14689         /**
14690          * Returns the measured height of the specified text.  For multiline text, be sure to call
14691          * {@link #setFixedWidth} if necessary.
14692          * @memberOf Roo.util.TextMetrics.Instance#
14693          * @param {String} text The text to measure
14694          * @return {Number} height The height in pixels
14695          */
14696         getHeight : function(text){
14697             return this.getSize(text).height;
14698         }
14699     };
14700
14701     instance.bind(bindTo);
14702
14703     return instance;
14704 };
14705
14706 // backwards compat
14707 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14708  * Based on:
14709  * Ext JS Library 1.1.1
14710  * Copyright(c) 2006-2007, Ext JS, LLC.
14711  *
14712  * Originally Released Under LGPL - original licence link has changed is not relivant.
14713  *
14714  * Fork - LGPL
14715  * <script type="text/javascript">
14716  */
14717
14718 /**
14719  * @class Roo.state.Provider
14720  * Abstract base class for state provider implementations. This class provides methods
14721  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14722  * Provider interface.
14723  */
14724 Roo.state.Provider = function(){
14725     /**
14726      * @event statechange
14727      * Fires when a state change occurs.
14728      * @param {Provider} this This state provider
14729      * @param {String} key The state key which was changed
14730      * @param {String} value The encoded value for the state
14731      */
14732     this.addEvents({
14733         "statechange": true
14734     });
14735     this.state = {};
14736     Roo.state.Provider.superclass.constructor.call(this);
14737 };
14738 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14739     /**
14740      * Returns the current value for a key
14741      * @param {String} name The key name
14742      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14743      * @return {Mixed} The state data
14744      */
14745     get : function(name, defaultValue){
14746         return typeof this.state[name] == "undefined" ?
14747             defaultValue : this.state[name];
14748     },
14749     
14750     /**
14751      * Clears a value from the state
14752      * @param {String} name The key name
14753      */
14754     clear : function(name){
14755         delete this.state[name];
14756         this.fireEvent("statechange", this, name, null);
14757     },
14758     
14759     /**
14760      * Sets the value for a key
14761      * @param {String} name The key name
14762      * @param {Mixed} value The value to set
14763      */
14764     set : function(name, value){
14765         this.state[name] = value;
14766         this.fireEvent("statechange", this, name, value);
14767     },
14768     
14769     /**
14770      * Decodes a string previously encoded with {@link #encodeValue}.
14771      * @param {String} value The value to decode
14772      * @return {Mixed} The decoded value
14773      */
14774     decodeValue : function(cookie){
14775         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14776         var matches = re.exec(unescape(cookie));
14777         if(!matches || !matches[1]) return; // non state cookie
14778         var type = matches[1];
14779         var v = matches[2];
14780         switch(type){
14781             case "n":
14782                 return parseFloat(v);
14783             case "d":
14784                 return new Date(Date.parse(v));
14785             case "b":
14786                 return (v == "1");
14787             case "a":
14788                 var all = [];
14789                 var values = v.split("^");
14790                 for(var i = 0, len = values.length; i < len; i++){
14791                     all.push(this.decodeValue(values[i]));
14792                 }
14793                 return all;
14794            case "o":
14795                 var all = {};
14796                 var values = v.split("^");
14797                 for(var i = 0, len = values.length; i < len; i++){
14798                     var kv = values[i].split("=");
14799                     all[kv[0]] = this.decodeValue(kv[1]);
14800                 }
14801                 return all;
14802            default:
14803                 return v;
14804         }
14805     },
14806     
14807     /**
14808      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14809      * @param {Mixed} value The value to encode
14810      * @return {String} The encoded value
14811      */
14812     encodeValue : function(v){
14813         var enc;
14814         if(typeof v == "number"){
14815             enc = "n:" + v;
14816         }else if(typeof v == "boolean"){
14817             enc = "b:" + (v ? "1" : "0");
14818         }else if(v instanceof Date){
14819             enc = "d:" + v.toGMTString();
14820         }else if(v instanceof Array){
14821             var flat = "";
14822             for(var i = 0, len = v.length; i < len; i++){
14823                 flat += this.encodeValue(v[i]);
14824                 if(i != len-1) flat += "^";
14825             }
14826             enc = "a:" + flat;
14827         }else if(typeof v == "object"){
14828             var flat = "";
14829             for(var key in v){
14830                 if(typeof v[key] != "function"){
14831                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14832                 }
14833             }
14834             enc = "o:" + flat.substring(0, flat.length-1);
14835         }else{
14836             enc = "s:" + v;
14837         }
14838         return escape(enc);        
14839     }
14840 });
14841
14842 /*
14843  * Based on:
14844  * Ext JS Library 1.1.1
14845  * Copyright(c) 2006-2007, Ext JS, LLC.
14846  *
14847  * Originally Released Under LGPL - original licence link has changed is not relivant.
14848  *
14849  * Fork - LGPL
14850  * <script type="text/javascript">
14851  */
14852 /**
14853  * @class Roo.state.Manager
14854  * This is the global state manager. By default all components that are "state aware" check this class
14855  * for state information if you don't pass them a custom state provider. In order for this class
14856  * to be useful, it must be initialized with a provider when your application initializes.
14857  <pre><code>
14858 // in your initialization function
14859 init : function(){
14860    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14861    ...
14862    // supposed you have a {@link Roo.BorderLayout}
14863    var layout = new Roo.BorderLayout(...);
14864    layout.restoreState();
14865    // or a {Roo.BasicDialog}
14866    var dialog = new Roo.BasicDialog(...);
14867    dialog.restoreState();
14868  </code></pre>
14869  * @singleton
14870  */
14871 Roo.state.Manager = function(){
14872     var provider = new Roo.state.Provider();
14873     
14874     return {
14875         /**
14876          * Configures the default state provider for your application
14877          * @param {Provider} stateProvider The state provider to set
14878          */
14879         setProvider : function(stateProvider){
14880             provider = stateProvider;
14881         },
14882         
14883         /**
14884          * Returns the current value for a key
14885          * @param {String} name The key name
14886          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14887          * @return {Mixed} The state data
14888          */
14889         get : function(key, defaultValue){
14890             return provider.get(key, defaultValue);
14891         },
14892         
14893         /**
14894          * Sets the value for a key
14895          * @param {String} name The key name
14896          * @param {Mixed} value The state data
14897          */
14898          set : function(key, value){
14899             provider.set(key, value);
14900         },
14901         
14902         /**
14903          * Clears a value from the state
14904          * @param {String} name The key name
14905          */
14906         clear : function(key){
14907             provider.clear(key);
14908         },
14909         
14910         /**
14911          * Gets the currently configured state provider
14912          * @return {Provider} The state provider
14913          */
14914         getProvider : function(){
14915             return provider;
14916         }
14917     };
14918 }();
14919 /*
14920  * Based on:
14921  * Ext JS Library 1.1.1
14922  * Copyright(c) 2006-2007, Ext JS, LLC.
14923  *
14924  * Originally Released Under LGPL - original licence link has changed is not relivant.
14925  *
14926  * Fork - LGPL
14927  * <script type="text/javascript">
14928  */
14929 /**
14930  * @class Roo.state.CookieProvider
14931  * @extends Roo.state.Provider
14932  * The default Provider implementation which saves state via cookies.
14933  * <br />Usage:
14934  <pre><code>
14935    var cp = new Roo.state.CookieProvider({
14936        path: "/cgi-bin/",
14937        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14938        domain: "roojs.com"
14939    })
14940    Roo.state.Manager.setProvider(cp);
14941  </code></pre>
14942  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14943  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14944  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14945  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14946  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14947  * domain the page is running on including the 'www' like 'www.roojs.com')
14948  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14949  * @constructor
14950  * Create a new CookieProvider
14951  * @param {Object} config The configuration object
14952  */
14953 Roo.state.CookieProvider = function(config){
14954     Roo.state.CookieProvider.superclass.constructor.call(this);
14955     this.path = "/";
14956     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14957     this.domain = null;
14958     this.secure = false;
14959     Roo.apply(this, config);
14960     this.state = this.readCookies();
14961 };
14962
14963 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14964     // private
14965     set : function(name, value){
14966         if(typeof value == "undefined" || value === null){
14967             this.clear(name);
14968             return;
14969         }
14970         this.setCookie(name, value);
14971         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14972     },
14973
14974     // private
14975     clear : function(name){
14976         this.clearCookie(name);
14977         Roo.state.CookieProvider.superclass.clear.call(this, name);
14978     },
14979
14980     // private
14981     readCookies : function(){
14982         var cookies = {};
14983         var c = document.cookie + ";";
14984         var re = /\s?(.*?)=(.*?);/g;
14985         var matches;
14986         while((matches = re.exec(c)) != null){
14987             var name = matches[1];
14988             var value = matches[2];
14989             if(name && name.substring(0,3) == "ys-"){
14990                 cookies[name.substr(3)] = this.decodeValue(value);
14991             }
14992         }
14993         return cookies;
14994     },
14995
14996     // private
14997     setCookie : function(name, value){
14998         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14999            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15000            ((this.path == null) ? "" : ("; path=" + this.path)) +
15001            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15002            ((this.secure == true) ? "; secure" : "");
15003     },
15004
15005     // private
15006     clearCookie : function(name){
15007         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15008            ((this.path == null) ? "" : ("; path=" + this.path)) +
15009            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15010            ((this.secure == true) ? "; secure" : "");
15011     }
15012 });/*
15013  * Based on:
15014  * Ext JS Library 1.1.1
15015  * Copyright(c) 2006-2007, Ext JS, LLC.
15016  *
15017  * Originally Released Under LGPL - original licence link has changed is not relivant.
15018  *
15019  * Fork - LGPL
15020  * <script type="text/javascript">
15021  */
15022  
15023
15024 /**
15025  * @class Roo.ComponentMgr
15026  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15027  * @singleton
15028  */
15029 Roo.ComponentMgr = function(){
15030     var all = new Roo.util.MixedCollection();
15031
15032     return {
15033         /**
15034          * Registers a component.
15035          * @param {Roo.Component} c The component
15036          */
15037         register : function(c){
15038             all.add(c);
15039         },
15040
15041         /**
15042          * Unregisters a component.
15043          * @param {Roo.Component} c The component
15044          */
15045         unregister : function(c){
15046             all.remove(c);
15047         },
15048
15049         /**
15050          * Returns a component by id
15051          * @param {String} id The component id
15052          */
15053         get : function(id){
15054             return all.get(id);
15055         },
15056
15057         /**
15058          * Registers a function that will be called when a specified component is added to ComponentMgr
15059          * @param {String} id The component id
15060          * @param {Funtction} fn The callback function
15061          * @param {Object} scope The scope of the callback
15062          */
15063         onAvailable : function(id, fn, scope){
15064             all.on("add", function(index, o){
15065                 if(o.id == id){
15066                     fn.call(scope || o, o);
15067                     all.un("add", fn, scope);
15068                 }
15069             });
15070         }
15071     };
15072 }();/*
15073  * Based on:
15074  * Ext JS Library 1.1.1
15075  * Copyright(c) 2006-2007, Ext JS, LLC.
15076  *
15077  * Originally Released Under LGPL - original licence link has changed is not relivant.
15078  *
15079  * Fork - LGPL
15080  * <script type="text/javascript">
15081  */
15082  
15083 /**
15084  * @class Roo.Component
15085  * @extends Roo.util.Observable
15086  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15087  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15088  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15089  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15090  * All visual components (widgets) that require rendering into a layout should subclass Component.
15091  * @constructor
15092  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15093  * 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
15094  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15095  */
15096 Roo.Component = function(config){
15097     config = config || {};
15098     if(config.tagName || config.dom || typeof config == "string"){ // element object
15099         config = {el: config, id: config.id || config};
15100     }
15101     this.initialConfig = config;
15102
15103     Roo.apply(this, config);
15104     this.addEvents({
15105         /**
15106          * @event disable
15107          * Fires after the component is disabled.
15108              * @param {Roo.Component} this
15109              */
15110         disable : true,
15111         /**
15112          * @event enable
15113          * Fires after the component is enabled.
15114              * @param {Roo.Component} this
15115              */
15116         enable : true,
15117         /**
15118          * @event beforeshow
15119          * Fires before the component is shown.  Return false to stop the show.
15120              * @param {Roo.Component} this
15121              */
15122         beforeshow : true,
15123         /**
15124          * @event show
15125          * Fires after the component is shown.
15126              * @param {Roo.Component} this
15127              */
15128         show : true,
15129         /**
15130          * @event beforehide
15131          * Fires before the component is hidden. Return false to stop the hide.
15132              * @param {Roo.Component} this
15133              */
15134         beforehide : true,
15135         /**
15136          * @event hide
15137          * Fires after the component is hidden.
15138              * @param {Roo.Component} this
15139              */
15140         hide : true,
15141         /**
15142          * @event beforerender
15143          * Fires before the component is rendered. Return false to stop the render.
15144              * @param {Roo.Component} this
15145              */
15146         beforerender : true,
15147         /**
15148          * @event render
15149          * Fires after the component is rendered.
15150              * @param {Roo.Component} this
15151              */
15152         render : true,
15153         /**
15154          * @event beforedestroy
15155          * Fires before the component is destroyed. Return false to stop the destroy.
15156              * @param {Roo.Component} this
15157              */
15158         beforedestroy : true,
15159         /**
15160          * @event destroy
15161          * Fires after the component is destroyed.
15162              * @param {Roo.Component} this
15163              */
15164         destroy : true
15165     });
15166     if(!this.id){
15167         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15168     }
15169     Roo.ComponentMgr.register(this);
15170     Roo.Component.superclass.constructor.call(this);
15171     this.initComponent();
15172     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15173         this.render(this.renderTo);
15174         delete this.renderTo;
15175     }
15176 };
15177
15178 /** @private */
15179 Roo.Component.AUTO_ID = 1000;
15180
15181 Roo.extend(Roo.Component, Roo.util.Observable, {
15182     /**
15183      * @scope Roo.Component.prototype
15184      * @type {Boolean}
15185      * true if this component is hidden. Read-only.
15186      */
15187     hidden : false,
15188     /**
15189      * @type {Boolean}
15190      * true if this component is disabled. Read-only.
15191      */
15192     disabled : false,
15193     /**
15194      * @type {Boolean}
15195      * true if this component has been rendered. Read-only.
15196      */
15197     rendered : false,
15198     
15199     /** @cfg {String} disableClass
15200      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15201      */
15202     disabledClass : "x-item-disabled",
15203         /** @cfg {Boolean} allowDomMove
15204          * Whether the component can move the Dom node when rendering (defaults to true).
15205          */
15206     allowDomMove : true,
15207     /** @cfg {String} hideMode
15208      * How this component should hidden. Supported values are
15209      * "visibility" (css visibility), "offsets" (negative offset position) and
15210      * "display" (css display) - defaults to "display".
15211      */
15212     hideMode: 'display',
15213
15214     /** @private */
15215     ctype : "Roo.Component",
15216
15217     /**
15218      * @cfg {String} actionMode 
15219      * which property holds the element that used for  hide() / show() / disable() / enable()
15220      * default is 'el' 
15221      */
15222     actionMode : "el",
15223
15224     /** @private */
15225     getActionEl : function(){
15226         return this[this.actionMode];
15227     },
15228
15229     initComponent : Roo.emptyFn,
15230     /**
15231      * If this is a lazy rendering component, render it to its container element.
15232      * @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.
15233      */
15234     render : function(container, position){
15235         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15236             if(!container && this.el){
15237                 this.el = Roo.get(this.el);
15238                 container = this.el.dom.parentNode;
15239                 this.allowDomMove = false;
15240             }
15241             this.container = Roo.get(container);
15242             this.rendered = true;
15243             if(position !== undefined){
15244                 if(typeof position == 'number'){
15245                     position = this.container.dom.childNodes[position];
15246                 }else{
15247                     position = Roo.getDom(position);
15248                 }
15249             }
15250             this.onRender(this.container, position || null);
15251             if(this.cls){
15252                 this.el.addClass(this.cls);
15253                 delete this.cls;
15254             }
15255             if(this.style){
15256                 this.el.applyStyles(this.style);
15257                 delete this.style;
15258             }
15259             this.fireEvent("render", this);
15260             this.afterRender(this.container);
15261             if(this.hidden){
15262                 this.hide();
15263             }
15264             if(this.disabled){
15265                 this.disable();
15266             }
15267         }
15268         return this;
15269     },
15270
15271     /** @private */
15272     // default function is not really useful
15273     onRender : function(ct, position){
15274         if(this.el){
15275             this.el = Roo.get(this.el);
15276             if(this.allowDomMove !== false){
15277                 ct.dom.insertBefore(this.el.dom, position);
15278             }
15279         }
15280     },
15281
15282     /** @private */
15283     getAutoCreate : function(){
15284         var cfg = typeof this.autoCreate == "object" ?
15285                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15286         if(this.id && !cfg.id){
15287             cfg.id = this.id;
15288         }
15289         return cfg;
15290     },
15291
15292     /** @private */
15293     afterRender : Roo.emptyFn,
15294
15295     /**
15296      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15297      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15298      */
15299     destroy : function(){
15300         if(this.fireEvent("beforedestroy", this) !== false){
15301             this.purgeListeners();
15302             this.beforeDestroy();
15303             if(this.rendered){
15304                 this.el.removeAllListeners();
15305                 this.el.remove();
15306                 if(this.actionMode == "container"){
15307                     this.container.remove();
15308                 }
15309             }
15310             this.onDestroy();
15311             Roo.ComponentMgr.unregister(this);
15312             this.fireEvent("destroy", this);
15313         }
15314     },
15315
15316         /** @private */
15317     beforeDestroy : function(){
15318
15319     },
15320
15321         /** @private */
15322         onDestroy : function(){
15323
15324     },
15325
15326     /**
15327      * Returns the underlying {@link Roo.Element}.
15328      * @return {Roo.Element} The element
15329      */
15330     getEl : function(){
15331         return this.el;
15332     },
15333
15334     /**
15335      * Returns the id of this component.
15336      * @return {String}
15337      */
15338     getId : function(){
15339         return this.id;
15340     },
15341
15342     /**
15343      * Try to focus this component.
15344      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15345      * @return {Roo.Component} this
15346      */
15347     focus : function(selectText){
15348         if(this.rendered){
15349             this.el.focus();
15350             if(selectText === true){
15351                 this.el.dom.select();
15352             }
15353         }
15354         return this;
15355     },
15356
15357     /** @private */
15358     blur : function(){
15359         if(this.rendered){
15360             this.el.blur();
15361         }
15362         return this;
15363     },
15364
15365     /**
15366      * Disable this component.
15367      * @return {Roo.Component} this
15368      */
15369     disable : function(){
15370         if(this.rendered){
15371             this.onDisable();
15372         }
15373         this.disabled = true;
15374         this.fireEvent("disable", this);
15375         return this;
15376     },
15377
15378         // private
15379     onDisable : function(){
15380         this.getActionEl().addClass(this.disabledClass);
15381         this.el.dom.disabled = true;
15382     },
15383
15384     /**
15385      * Enable this component.
15386      * @return {Roo.Component} this
15387      */
15388     enable : function(){
15389         if(this.rendered){
15390             this.onEnable();
15391         }
15392         this.disabled = false;
15393         this.fireEvent("enable", this);
15394         return this;
15395     },
15396
15397         // private
15398     onEnable : function(){
15399         this.getActionEl().removeClass(this.disabledClass);
15400         this.el.dom.disabled = false;
15401     },
15402
15403     /**
15404      * Convenience function for setting disabled/enabled by boolean.
15405      * @param {Boolean} disabled
15406      */
15407     setDisabled : function(disabled){
15408         this[disabled ? "disable" : "enable"]();
15409     },
15410
15411     /**
15412      * Show this component.
15413      * @return {Roo.Component} this
15414      */
15415     show: function(){
15416         if(this.fireEvent("beforeshow", this) !== false){
15417             this.hidden = false;
15418             if(this.rendered){
15419                 this.onShow();
15420             }
15421             this.fireEvent("show", this);
15422         }
15423         return this;
15424     },
15425
15426     // private
15427     onShow : function(){
15428         var ae = this.getActionEl();
15429         if(this.hideMode == 'visibility'){
15430             ae.dom.style.visibility = "visible";
15431         }else if(this.hideMode == 'offsets'){
15432             ae.removeClass('x-hidden');
15433         }else{
15434             ae.dom.style.display = "";
15435         }
15436     },
15437
15438     /**
15439      * Hide this component.
15440      * @return {Roo.Component} this
15441      */
15442     hide: function(){
15443         if(this.fireEvent("beforehide", this) !== false){
15444             this.hidden = true;
15445             if(this.rendered){
15446                 this.onHide();
15447             }
15448             this.fireEvent("hide", this);
15449         }
15450         return this;
15451     },
15452
15453     // private
15454     onHide : function(){
15455         var ae = this.getActionEl();
15456         if(this.hideMode == 'visibility'){
15457             ae.dom.style.visibility = "hidden";
15458         }else if(this.hideMode == 'offsets'){
15459             ae.addClass('x-hidden');
15460         }else{
15461             ae.dom.style.display = "none";
15462         }
15463     },
15464
15465     /**
15466      * Convenience function to hide or show this component by boolean.
15467      * @param {Boolean} visible True to show, false to hide
15468      * @return {Roo.Component} this
15469      */
15470     setVisible: function(visible){
15471         if(visible) {
15472             this.show();
15473         }else{
15474             this.hide();
15475         }
15476         return this;
15477     },
15478
15479     /**
15480      * Returns true if this component is visible.
15481      */
15482     isVisible : function(){
15483         return this.getActionEl().isVisible();
15484     },
15485
15486     cloneConfig : function(overrides){
15487         overrides = overrides || {};
15488         var id = overrides.id || Roo.id();
15489         var cfg = Roo.applyIf(overrides, this.initialConfig);
15490         cfg.id = id; // prevent dup id
15491         return new this.constructor(cfg);
15492     }
15493 });/*
15494  * Based on:
15495  * Ext JS Library 1.1.1
15496  * Copyright(c) 2006-2007, Ext JS, LLC.
15497  *
15498  * Originally Released Under LGPL - original licence link has changed is not relivant.
15499  *
15500  * Fork - LGPL
15501  * <script type="text/javascript">
15502  */
15503
15504 /**
15505  * @class Roo.BoxComponent
15506  * @extends Roo.Component
15507  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15508  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15509  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15510  * layout containers.
15511  * @constructor
15512  * @param {Roo.Element/String/Object} config The configuration options.
15513  */
15514 Roo.BoxComponent = function(config){
15515     Roo.Component.call(this, config);
15516     this.addEvents({
15517         /**
15518          * @event resize
15519          * Fires after the component is resized.
15520              * @param {Roo.Component} this
15521              * @param {Number} adjWidth The box-adjusted width that was set
15522              * @param {Number} adjHeight The box-adjusted height that was set
15523              * @param {Number} rawWidth The width that was originally specified
15524              * @param {Number} rawHeight The height that was originally specified
15525              */
15526         resize : true,
15527         /**
15528          * @event move
15529          * Fires after the component is moved.
15530              * @param {Roo.Component} this
15531              * @param {Number} x The new x position
15532              * @param {Number} y The new y position
15533              */
15534         move : true
15535     });
15536 };
15537
15538 Roo.extend(Roo.BoxComponent, Roo.Component, {
15539     // private, set in afterRender to signify that the component has been rendered
15540     boxReady : false,
15541     // private, used to defer height settings to subclasses
15542     deferHeight: false,
15543     /** @cfg {Number} width
15544      * width (optional) size of component
15545      */
15546      /** @cfg {Number} height
15547      * height (optional) size of component
15548      */
15549      
15550     /**
15551      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15552      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15553      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15554      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15555      * @return {Roo.BoxComponent} this
15556      */
15557     setSize : function(w, h){
15558         // support for standard size objects
15559         if(typeof w == 'object'){
15560             h = w.height;
15561             w = w.width;
15562         }
15563         // not rendered
15564         if(!this.boxReady){
15565             this.width = w;
15566             this.height = h;
15567             return this;
15568         }
15569
15570         // prevent recalcs when not needed
15571         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15572             return this;
15573         }
15574         this.lastSize = {width: w, height: h};
15575
15576         var adj = this.adjustSize(w, h);
15577         var aw = adj.width, ah = adj.height;
15578         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15579             var rz = this.getResizeEl();
15580             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15581                 rz.setSize(aw, ah);
15582             }else if(!this.deferHeight && ah !== undefined){
15583                 rz.setHeight(ah);
15584             }else if(aw !== undefined){
15585                 rz.setWidth(aw);
15586             }
15587             this.onResize(aw, ah, w, h);
15588             this.fireEvent('resize', this, aw, ah, w, h);
15589         }
15590         return this;
15591     },
15592
15593     /**
15594      * Gets the current size of the component's underlying element.
15595      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15596      */
15597     getSize : function(){
15598         return this.el.getSize();
15599     },
15600
15601     /**
15602      * Gets the current XY position of the component's underlying element.
15603      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15604      * @return {Array} The XY position of the element (e.g., [100, 200])
15605      */
15606     getPosition : function(local){
15607         if(local === true){
15608             return [this.el.getLeft(true), this.el.getTop(true)];
15609         }
15610         return this.xy || this.el.getXY();
15611     },
15612
15613     /**
15614      * Gets the current box measurements of the component's underlying element.
15615      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15616      * @returns {Object} box An object in the format {x, y, width, height}
15617      */
15618     getBox : function(local){
15619         var s = this.el.getSize();
15620         if(local){
15621             s.x = this.el.getLeft(true);
15622             s.y = this.el.getTop(true);
15623         }else{
15624             var xy = this.xy || this.el.getXY();
15625             s.x = xy[0];
15626             s.y = xy[1];
15627         }
15628         return s;
15629     },
15630
15631     /**
15632      * Sets the current box measurements of the component's underlying element.
15633      * @param {Object} box An object in the format {x, y, width, height}
15634      * @returns {Roo.BoxComponent} this
15635      */
15636     updateBox : function(box){
15637         this.setSize(box.width, box.height);
15638         this.setPagePosition(box.x, box.y);
15639         return this;
15640     },
15641
15642     // protected
15643     getResizeEl : function(){
15644         return this.resizeEl || this.el;
15645     },
15646
15647     // protected
15648     getPositionEl : function(){
15649         return this.positionEl || this.el;
15650     },
15651
15652     /**
15653      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15654      * This method fires the move event.
15655      * @param {Number} left The new left
15656      * @param {Number} top The new top
15657      * @returns {Roo.BoxComponent} this
15658      */
15659     setPosition : function(x, y){
15660         this.x = x;
15661         this.y = y;
15662         if(!this.boxReady){
15663             return this;
15664         }
15665         var adj = this.adjustPosition(x, y);
15666         var ax = adj.x, ay = adj.y;
15667
15668         var el = this.getPositionEl();
15669         if(ax !== undefined || ay !== undefined){
15670             if(ax !== undefined && ay !== undefined){
15671                 el.setLeftTop(ax, ay);
15672             }else if(ax !== undefined){
15673                 el.setLeft(ax);
15674             }else if(ay !== undefined){
15675                 el.setTop(ay);
15676             }
15677             this.onPosition(ax, ay);
15678             this.fireEvent('move', this, ax, ay);
15679         }
15680         return this;
15681     },
15682
15683     /**
15684      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15685      * This method fires the move event.
15686      * @param {Number} x The new x position
15687      * @param {Number} y The new y position
15688      * @returns {Roo.BoxComponent} this
15689      */
15690     setPagePosition : function(x, y){
15691         this.pageX = x;
15692         this.pageY = y;
15693         if(!this.boxReady){
15694             return;
15695         }
15696         if(x === undefined || y === undefined){ // cannot translate undefined points
15697             return;
15698         }
15699         var p = this.el.translatePoints(x, y);
15700         this.setPosition(p.left, p.top);
15701         return this;
15702     },
15703
15704     // private
15705     onRender : function(ct, position){
15706         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15707         if(this.resizeEl){
15708             this.resizeEl = Roo.get(this.resizeEl);
15709         }
15710         if(this.positionEl){
15711             this.positionEl = Roo.get(this.positionEl);
15712         }
15713     },
15714
15715     // private
15716     afterRender : function(){
15717         Roo.BoxComponent.superclass.afterRender.call(this);
15718         this.boxReady = true;
15719         this.setSize(this.width, this.height);
15720         if(this.x || this.y){
15721             this.setPosition(this.x, this.y);
15722         }
15723         if(this.pageX || this.pageY){
15724             this.setPagePosition(this.pageX, this.pageY);
15725         }
15726     },
15727
15728     /**
15729      * Force the component's size to recalculate based on the underlying element's current height and width.
15730      * @returns {Roo.BoxComponent} this
15731      */
15732     syncSize : function(){
15733         delete this.lastSize;
15734         this.setSize(this.el.getWidth(), this.el.getHeight());
15735         return this;
15736     },
15737
15738     /**
15739      * Called after the component is resized, this method is empty by default but can be implemented by any
15740      * subclass that needs to perform custom logic after a resize occurs.
15741      * @param {Number} adjWidth The box-adjusted width that was set
15742      * @param {Number} adjHeight The box-adjusted height that was set
15743      * @param {Number} rawWidth The width that was originally specified
15744      * @param {Number} rawHeight The height that was originally specified
15745      */
15746     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15747
15748     },
15749
15750     /**
15751      * Called after the component is moved, this method is empty by default but can be implemented by any
15752      * subclass that needs to perform custom logic after a move occurs.
15753      * @param {Number} x The new x position
15754      * @param {Number} y The new y position
15755      */
15756     onPosition : function(x, y){
15757
15758     },
15759
15760     // private
15761     adjustSize : function(w, h){
15762         if(this.autoWidth){
15763             w = 'auto';
15764         }
15765         if(this.autoHeight){
15766             h = 'auto';
15767         }
15768         return {width : w, height: h};
15769     },
15770
15771     // private
15772     adjustPosition : function(x, y){
15773         return {x : x, y: y};
15774     }
15775 });/*
15776  * Original code for Roojs - LGPL
15777  * <script type="text/javascript">
15778  */
15779  
15780 /**
15781  * @class Roo.XComponent
15782  * A delayed Element creator...
15783  * Or a way to group chunks of interface together.
15784  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15785  *  used in conjunction with XComponent.build() it will create an instance of each element,
15786  *  then call addxtype() to build the User interface.
15787  * 
15788  * Mypart.xyx = new Roo.XComponent({
15789
15790     parent : 'Mypart.xyz', // empty == document.element.!!
15791     order : '001',
15792     name : 'xxxx'
15793     region : 'xxxx'
15794     disabled : function() {} 
15795      
15796     tree : function() { // return an tree of xtype declared components
15797         var MODULE = this;
15798         return 
15799         {
15800             xtype : 'NestedLayoutPanel',
15801             // technicall
15802         }
15803      ]
15804  *})
15805  *
15806  *
15807  * It can be used to build a big heiracy, with parent etc.
15808  * or you can just use this to render a single compoent to a dom element
15809  * MYPART.render(Roo.Element | String(id) | dom_element )
15810  *
15811  *
15812  * Usage patterns.
15813  *
15814  * Classic Roo
15815  *
15816  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15817  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15818  *
15819  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15820  *
15821  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15822  * - if mulitple topModules exist, the last one is defined as the top module.
15823  *
15824  * Embeded Roo
15825  * 
15826  * When the top level or multiple modules are to embedded into a existing HTML page,
15827  * the parent element can container '#id' of the element where the module will be drawn.
15828  *
15829  * Bootstrap Roo
15830  *
15831  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15832  * it relies more on a include mechanism, where sub modules are included into an outer page.
15833  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15834  * 
15835  * Bootstrap Roo Included elements
15836  *
15837  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15838  * hence confusing the component builder as it thinks there are multiple top level elements. 
15839  *
15840  * 
15841  * 
15842  * @extends Roo.util.Observable
15843  * @constructor
15844  * @param cfg {Object} configuration of component
15845  * 
15846  */
15847 Roo.XComponent = function(cfg) {
15848     Roo.apply(this, cfg);
15849     this.addEvents({ 
15850         /**
15851              * @event built
15852              * Fires when this the componnt is built
15853              * @param {Roo.XComponent} c the component
15854              */
15855         'built' : true
15856         
15857     });
15858     this.region = this.region || 'center'; // default..
15859     Roo.XComponent.register(this);
15860     this.modules = false;
15861     this.el = false; // where the layout goes..
15862     
15863     
15864 }
15865 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15866     /**
15867      * @property el
15868      * The created element (with Roo.factory())
15869      * @type {Roo.Layout}
15870      */
15871     el  : false,
15872     
15873     /**
15874      * @property el
15875      * for BC  - use el in new code
15876      * @type {Roo.Layout}
15877      */
15878     panel : false,
15879     
15880     /**
15881      * @property layout
15882      * for BC  - use el in new code
15883      * @type {Roo.Layout}
15884      */
15885     layout : false,
15886     
15887      /**
15888      * @cfg {Function|boolean} disabled
15889      * If this module is disabled by some rule, return true from the funtion
15890      */
15891     disabled : false,
15892     
15893     /**
15894      * @cfg {String} parent 
15895      * Name of parent element which it get xtype added to..
15896      */
15897     parent: false,
15898     
15899     /**
15900      * @cfg {String} order
15901      * Used to set the order in which elements are created (usefull for multiple tabs)
15902      */
15903     
15904     order : false,
15905     /**
15906      * @cfg {String} name
15907      * String to display while loading.
15908      */
15909     name : false,
15910     /**
15911      * @cfg {String} region
15912      * Region to render component to (defaults to center)
15913      */
15914     region : 'center',
15915     
15916     /**
15917      * @cfg {Array} items
15918      * A single item array - the first element is the root of the tree..
15919      * It's done this way to stay compatible with the Xtype system...
15920      */
15921     items : false,
15922     
15923     /**
15924      * @property _tree
15925      * The method that retuns the tree of parts that make up this compoennt 
15926      * @type {function}
15927      */
15928     _tree  : false,
15929     
15930      /**
15931      * render
15932      * render element to dom or tree
15933      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15934      */
15935     
15936     render : function(el)
15937     {
15938         
15939         el = el || false;
15940         var hp = this.parent ? 1 : 0;
15941         Roo.debug &&  Roo.log(this);
15942         
15943         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15944             // if parent is a '#.....' string, then let's use that..
15945             var ename = this.parent.substr(1);
15946             this.parent = false;
15947             Roo.debug && Roo.log(ename);
15948             switch (ename) {
15949                 case 'bootstrap-body' :
15950                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15951                         this.parent = { el :  new  Roo.bootstrap.Body() };
15952                         Roo.debug && Roo.log("setting el to doc body");
15953                          
15954                     } else {
15955                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15956                     }
15957                     break;
15958                 case 'bootstrap':
15959                     this.parent = { el : true};
15960                     // fall through
15961                 default:
15962                     el = Roo.get(ename);
15963                     break;
15964             }
15965                 
15966             
15967             if (!el && !this.parent) {
15968                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15969                 return;
15970             }
15971         }
15972         Roo.debug && Roo.log("EL:");
15973         Roo.debug && Roo.log(el);
15974         Roo.debug && Roo.log("this.parent.el:");
15975         Roo.debug && Roo.log(this.parent.el);
15976         
15977         var tree = this._tree ? this._tree() : this.tree();
15978
15979         // altertive root elements ??? - we need a better way to indicate these.
15980         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15981                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15982         
15983         if (!this.parent && is_alt) {
15984             //el = Roo.get(document.body);
15985             this.parent = { el : true };
15986         }
15987             
15988             
15989         
15990         if (!this.parent) {
15991             
15992             Roo.debug && Roo.log("no parent - creating one");
15993             
15994             el = el ? Roo.get(el) : false;      
15995             
15996             // it's a top level one..
15997             this.parent =  {
15998                 el : new Roo.BorderLayout(el || document.body, {
15999                 
16000                      center: {
16001                          titlebar: false,
16002                          autoScroll:false,
16003                          closeOnTab: true,
16004                          tabPosition: 'top',
16005                           //resizeTabs: true,
16006                          alwaysShowTabs: el && hp? false :  true,
16007                          hideTabs: el || !hp ? true :  false,
16008                          minTabWidth: 140
16009                      }
16010                  })
16011             }
16012         }
16013         
16014         if (!this.parent.el) {
16015                 // probably an old style ctor, which has been disabled.
16016                 return;
16017
16018         }
16019                 // The 'tree' method is  '_tree now' 
16020             
16021         tree.region = tree.region || this.region;
16022         
16023         if (this.parent.el === true) {
16024             // bootstrap... - body..
16025             this.parent.el = Roo.factory(tree);
16026         }
16027         
16028         this.el = this.parent.el.addxtype(tree);
16029         this.fireEvent('built', this);
16030         
16031         this.panel = this.el;
16032         this.layout = this.panel.layout;
16033         this.parentLayout = this.parent.layout  || false;  
16034          
16035     }
16036     
16037 });
16038
16039 Roo.apply(Roo.XComponent, {
16040     /**
16041      * @property  hideProgress
16042      * true to disable the building progress bar.. usefull on single page renders.
16043      * @type Boolean
16044      */
16045     hideProgress : false,
16046     /**
16047      * @property  buildCompleted
16048      * True when the builder has completed building the interface.
16049      * @type Boolean
16050      */
16051     buildCompleted : false,
16052      
16053     /**
16054      * @property  topModule
16055      * the upper most module - uses document.element as it's constructor.
16056      * @type Object
16057      */
16058      
16059     topModule  : false,
16060       
16061     /**
16062      * @property  modules
16063      * array of modules to be created by registration system.
16064      * @type {Array} of Roo.XComponent
16065      */
16066     
16067     modules : [],
16068     /**
16069      * @property  elmodules
16070      * array of modules to be created by which use #ID 
16071      * @type {Array} of Roo.XComponent
16072      */
16073      
16074     elmodules : [],
16075
16076      /**
16077      * @property  build_from_html
16078      * Build elements from html - used by bootstrap HTML stuff 
16079      *    - this is cleared after build is completed
16080      * @type {boolean} true  (default false)
16081      */
16082      
16083     build_from_html : false,
16084
16085     /**
16086      * Register components to be built later.
16087      *
16088      * This solves the following issues
16089      * - Building is not done on page load, but after an authentication process has occured.
16090      * - Interface elements are registered on page load
16091      * - Parent Interface elements may not be loaded before child, so this handles that..
16092      * 
16093      *
16094      * example:
16095      * 
16096      * MyApp.register({
16097           order : '000001',
16098           module : 'Pman.Tab.projectMgr',
16099           region : 'center',
16100           parent : 'Pman.layout',
16101           disabled : false,  // or use a function..
16102         })
16103      
16104      * * @param {Object} details about module
16105      */
16106     register : function(obj) {
16107                 
16108         Roo.XComponent.event.fireEvent('register', obj);
16109         switch(typeof(obj.disabled) ) {
16110                 
16111             case 'undefined':
16112                 break;
16113             
16114             case 'function':
16115                 if ( obj.disabled() ) {
16116                         return;
16117                 }
16118                 break;
16119             
16120             default:
16121                 if (obj.disabled) {
16122                         return;
16123                 }
16124                 break;
16125         }
16126                 
16127         this.modules.push(obj);
16128          
16129     },
16130     /**
16131      * convert a string to an object..
16132      * eg. 'AAA.BBB' -> finds AAA.BBB
16133
16134      */
16135     
16136     toObject : function(str)
16137     {
16138         if (!str || typeof(str) == 'object') {
16139             return str;
16140         }
16141         if (str.substring(0,1) == '#') {
16142             return str;
16143         }
16144
16145         var ar = str.split('.');
16146         var rt, o;
16147         rt = ar.shift();
16148             /** eval:var:o */
16149         try {
16150             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16151         } catch (e) {
16152             throw "Module not found : " + str;
16153         }
16154         
16155         if (o === false) {
16156             throw "Module not found : " + str;
16157         }
16158         Roo.each(ar, function(e) {
16159             if (typeof(o[e]) == 'undefined') {
16160                 throw "Module not found : " + str;
16161             }
16162             o = o[e];
16163         });
16164         
16165         return o;
16166         
16167     },
16168     
16169     
16170     /**
16171      * move modules into their correct place in the tree..
16172      * 
16173      */
16174     preBuild : function ()
16175     {
16176         var _t = this;
16177         Roo.each(this.modules , function (obj)
16178         {
16179             Roo.XComponent.event.fireEvent('beforebuild', obj);
16180             
16181             var opar = obj.parent;
16182             try { 
16183                 obj.parent = this.toObject(opar);
16184             } catch(e) {
16185                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16186                 return;
16187             }
16188             
16189             if (!obj.parent) {
16190                 Roo.debug && Roo.log("GOT top level module");
16191                 Roo.debug && Roo.log(obj);
16192                 obj.modules = new Roo.util.MixedCollection(false, 
16193                     function(o) { return o.order + '' }
16194                 );
16195                 this.topModule = obj;
16196                 return;
16197             }
16198                         // parent is a string (usually a dom element name..)
16199             if (typeof(obj.parent) == 'string') {
16200                 this.elmodules.push(obj);
16201                 return;
16202             }
16203             if (obj.parent.constructor != Roo.XComponent) {
16204                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16205             }
16206             if (!obj.parent.modules) {
16207                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16208                     function(o) { return o.order + '' }
16209                 );
16210             }
16211             if (obj.parent.disabled) {
16212                 obj.disabled = true;
16213             }
16214             obj.parent.modules.add(obj);
16215         }, this);
16216     },
16217     
16218      /**
16219      * make a list of modules to build.
16220      * @return {Array} list of modules. 
16221      */ 
16222     
16223     buildOrder : function()
16224     {
16225         var _this = this;
16226         var cmp = function(a,b) {   
16227             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16228         };
16229         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16230             throw "No top level modules to build";
16231         }
16232         
16233         // make a flat list in order of modules to build.
16234         var mods = this.topModule ? [ this.topModule ] : [];
16235                 
16236         
16237         // elmodules (is a list of DOM based modules )
16238         Roo.each(this.elmodules, function(e) {
16239             mods.push(e);
16240             if (!this.topModule &&
16241                 typeof(e.parent) == 'string' &&
16242                 e.parent.substring(0,1) == '#' &&
16243                 Roo.get(e.parent.substr(1))
16244                ) {
16245                 
16246                 _this.topModule = e;
16247             }
16248             
16249         });
16250
16251         
16252         // add modules to their parents..
16253         var addMod = function(m) {
16254             Roo.debug && Roo.log("build Order: add: " + m.name);
16255                 
16256             mods.push(m);
16257             if (m.modules && !m.disabled) {
16258                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16259                 m.modules.keySort('ASC',  cmp );
16260                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16261     
16262                 m.modules.each(addMod);
16263             } else {
16264                 Roo.debug && Roo.log("build Order: no child modules");
16265             }
16266             // not sure if this is used any more..
16267             if (m.finalize) {
16268                 m.finalize.name = m.name + " (clean up) ";
16269                 mods.push(m.finalize);
16270             }
16271             
16272         }
16273         if (this.topModule && this.topModule.modules) { 
16274             this.topModule.modules.keySort('ASC',  cmp );
16275             this.topModule.modules.each(addMod);
16276         } 
16277         return mods;
16278     },
16279     
16280      /**
16281      * Build the registered modules.
16282      * @param {Object} parent element.
16283      * @param {Function} optional method to call after module has been added.
16284      * 
16285      */ 
16286    
16287     build : function(opts) 
16288     {
16289         
16290         if (typeof(opts) != 'undefined') {
16291             Roo.apply(this,opts);
16292         }
16293         
16294         this.preBuild();
16295         var mods = this.buildOrder();
16296       
16297         //this.allmods = mods;
16298         //Roo.debug && Roo.log(mods);
16299         //return;
16300         if (!mods.length) { // should not happen
16301             throw "NO modules!!!";
16302         }
16303         
16304         
16305         var msg = "Building Interface...";
16306         // flash it up as modal - so we store the mask!?
16307         if (!this.hideProgress && Roo.MessageBox) {
16308             Roo.MessageBox.show({ title: 'loading' });
16309             Roo.MessageBox.show({
16310                title: "Please wait...",
16311                msg: msg,
16312                width:450,
16313                progress:true,
16314                closable:false,
16315                modal: false
16316               
16317             });
16318         }
16319         var total = mods.length;
16320         
16321         var _this = this;
16322         var progressRun = function() {
16323             if (!mods.length) {
16324                 Roo.debug && Roo.log('hide?');
16325                 if (!this.hideProgress && Roo.MessageBox) {
16326                     Roo.MessageBox.hide();
16327                 }
16328                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16329                 
16330                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16331                 
16332                 // THE END...
16333                 return false;   
16334             }
16335             
16336             var m = mods.shift();
16337             
16338             
16339             Roo.debug && Roo.log(m);
16340             // not sure if this is supported any more.. - modules that are are just function
16341             if (typeof(m) == 'function') { 
16342                 m.call(this);
16343                 return progressRun.defer(10, _this);
16344             } 
16345             
16346             
16347             msg = "Building Interface " + (total  - mods.length) + 
16348                     " of " + total + 
16349                     (m.name ? (' - ' + m.name) : '');
16350                         Roo.debug && Roo.log(msg);
16351             if (!this.hideProgress &&  Roo.MessageBox) { 
16352                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16353             }
16354             
16355          
16356             // is the module disabled?
16357             var disabled = (typeof(m.disabled) == 'function') ?
16358                 m.disabled.call(m.module.disabled) : m.disabled;    
16359             
16360             
16361             if (disabled) {
16362                 return progressRun(); // we do not update the display!
16363             }
16364             
16365             // now build 
16366             
16367                         
16368                         
16369             m.render();
16370             // it's 10 on top level, and 1 on others??? why...
16371             return progressRun.defer(10, _this);
16372              
16373         }
16374         progressRun.defer(1, _this);
16375      
16376         
16377         
16378     },
16379         
16380         
16381         /**
16382          * Event Object.
16383          *
16384          *
16385          */
16386         event: false, 
16387     /**
16388          * wrapper for event.on - aliased later..  
16389          * Typically use to register a event handler for register:
16390          *
16391          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16392          *
16393          */
16394     on : false
16395    
16396     
16397     
16398 });
16399
16400 Roo.XComponent.event = new Roo.util.Observable({
16401                 events : { 
16402                         /**
16403                          * @event register
16404                          * Fires when an Component is registered,
16405                          * set the disable property on the Component to stop registration.
16406                          * @param {Roo.XComponent} c the component being registerd.
16407                          * 
16408                          */
16409                         'register' : true,
16410             /**
16411                          * @event beforebuild
16412                          * Fires before each Component is built
16413                          * can be used to apply permissions.
16414                          * @param {Roo.XComponent} c the component being registerd.
16415                          * 
16416                          */
16417                         'beforebuild' : true,
16418                         /**
16419                          * @event buildcomplete
16420                          * Fires on the top level element when all elements have been built
16421                          * @param {Roo.XComponent} the top level component.
16422                          */
16423                         'buildcomplete' : true
16424                         
16425                 }
16426 });
16427
16428 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16429  /*
16430  * Based on:
16431  * Ext JS Library 1.1.1
16432  * Copyright(c) 2006-2007, Ext JS, LLC.
16433  *
16434  * Originally Released Under LGPL - original licence link has changed is not relivant.
16435  *
16436  * Fork - LGPL
16437  * <script type="text/javascript">
16438  */
16439
16440
16441
16442 /*
16443  * These classes are derivatives of the similarly named classes in the YUI Library.
16444  * The original license:
16445  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16446  * Code licensed under the BSD License:
16447  * http://developer.yahoo.net/yui/license.txt
16448  */
16449
16450 (function() {
16451
16452 var Event=Roo.EventManager;
16453 var Dom=Roo.lib.Dom;
16454
16455 /**
16456  * @class Roo.dd.DragDrop
16457  * @extends Roo.util.Observable
16458  * Defines the interface and base operation of items that that can be
16459  * dragged or can be drop targets.  It was designed to be extended, overriding
16460  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16461  * Up to three html elements can be associated with a DragDrop instance:
16462  * <ul>
16463  * <li>linked element: the element that is passed into the constructor.
16464  * This is the element which defines the boundaries for interaction with
16465  * other DragDrop objects.</li>
16466  * <li>handle element(s): The drag operation only occurs if the element that
16467  * was clicked matches a handle element.  By default this is the linked
16468  * element, but there are times that you will want only a portion of the
16469  * linked element to initiate the drag operation, and the setHandleElId()
16470  * method provides a way to define this.</li>
16471  * <li>drag element: this represents the element that would be moved along
16472  * with the cursor during a drag operation.  By default, this is the linked
16473  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16474  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16475  * </li>
16476  * </ul>
16477  * This class should not be instantiated until the onload event to ensure that
16478  * the associated elements are available.
16479  * The following would define a DragDrop obj that would interact with any
16480  * other DragDrop obj in the "group1" group:
16481  * <pre>
16482  *  dd = new Roo.dd.DragDrop("div1", "group1");
16483  * </pre>
16484  * Since none of the event handlers have been implemented, nothing would
16485  * actually happen if you were to run the code above.  Normally you would
16486  * override this class or one of the default implementations, but you can
16487  * also override the methods you want on an instance of the class...
16488  * <pre>
16489  *  dd.onDragDrop = function(e, id) {
16490  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16491  *  }
16492  * </pre>
16493  * @constructor
16494  * @param {String} id of the element that is linked to this instance
16495  * @param {String} sGroup the group of related DragDrop objects
16496  * @param {object} config an object containing configurable attributes
16497  *                Valid properties for DragDrop:
16498  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16499  */
16500 Roo.dd.DragDrop = function(id, sGroup, config) {
16501     if (id) {
16502         this.init(id, sGroup, config);
16503     }
16504     
16505 };
16506
16507 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16508
16509     /**
16510      * The id of the element associated with this object.  This is what we
16511      * refer to as the "linked element" because the size and position of
16512      * this element is used to determine when the drag and drop objects have
16513      * interacted.
16514      * @property id
16515      * @type String
16516      */
16517     id: null,
16518
16519     /**
16520      * Configuration attributes passed into the constructor
16521      * @property config
16522      * @type object
16523      */
16524     config: null,
16525
16526     /**
16527      * The id of the element that will be dragged.  By default this is same
16528      * as the linked element , but could be changed to another element. Ex:
16529      * Roo.dd.DDProxy
16530      * @property dragElId
16531      * @type String
16532      * @private
16533      */
16534     dragElId: null,
16535
16536     /**
16537      * the id of the element that initiates the drag operation.  By default
16538      * this is the linked element, but could be changed to be a child of this
16539      * element.  This lets us do things like only starting the drag when the
16540      * header element within the linked html element is clicked.
16541      * @property handleElId
16542      * @type String
16543      * @private
16544      */
16545     handleElId: null,
16546
16547     /**
16548      * An associative array of HTML tags that will be ignored if clicked.
16549      * @property invalidHandleTypes
16550      * @type {string: string}
16551      */
16552     invalidHandleTypes: null,
16553
16554     /**
16555      * An associative array of ids for elements that will be ignored if clicked
16556      * @property invalidHandleIds
16557      * @type {string: string}
16558      */
16559     invalidHandleIds: null,
16560
16561     /**
16562      * An indexted array of css class names for elements that will be ignored
16563      * if clicked.
16564      * @property invalidHandleClasses
16565      * @type string[]
16566      */
16567     invalidHandleClasses: null,
16568
16569     /**
16570      * The linked element's absolute X position at the time the drag was
16571      * started
16572      * @property startPageX
16573      * @type int
16574      * @private
16575      */
16576     startPageX: 0,
16577
16578     /**
16579      * The linked element's absolute X position at the time the drag was
16580      * started
16581      * @property startPageY
16582      * @type int
16583      * @private
16584      */
16585     startPageY: 0,
16586
16587     /**
16588      * The group defines a logical collection of DragDrop objects that are
16589      * related.  Instances only get events when interacting with other
16590      * DragDrop object in the same group.  This lets us define multiple
16591      * groups using a single DragDrop subclass if we want.
16592      * @property groups
16593      * @type {string: string}
16594      */
16595     groups: null,
16596
16597     /**
16598      * Individual drag/drop instances can be locked.  This will prevent
16599      * onmousedown start drag.
16600      * @property locked
16601      * @type boolean
16602      * @private
16603      */
16604     locked: false,
16605
16606     /**
16607      * Lock this instance
16608      * @method lock
16609      */
16610     lock: function() { this.locked = true; },
16611
16612     /**
16613      * Unlock this instace
16614      * @method unlock
16615      */
16616     unlock: function() { this.locked = false; },
16617
16618     /**
16619      * By default, all insances can be a drop target.  This can be disabled by
16620      * setting isTarget to false.
16621      * @method isTarget
16622      * @type boolean
16623      */
16624     isTarget: true,
16625
16626     /**
16627      * The padding configured for this drag and drop object for calculating
16628      * the drop zone intersection with this object.
16629      * @method padding
16630      * @type int[]
16631      */
16632     padding: null,
16633
16634     /**
16635      * Cached reference to the linked element
16636      * @property _domRef
16637      * @private
16638      */
16639     _domRef: null,
16640
16641     /**
16642      * Internal typeof flag
16643      * @property __ygDragDrop
16644      * @private
16645      */
16646     __ygDragDrop: true,
16647
16648     /**
16649      * Set to true when horizontal contraints are applied
16650      * @property constrainX
16651      * @type boolean
16652      * @private
16653      */
16654     constrainX: false,
16655
16656     /**
16657      * Set to true when vertical contraints are applied
16658      * @property constrainY
16659      * @type boolean
16660      * @private
16661      */
16662     constrainY: false,
16663
16664     /**
16665      * The left constraint
16666      * @property minX
16667      * @type int
16668      * @private
16669      */
16670     minX: 0,
16671
16672     /**
16673      * The right constraint
16674      * @property maxX
16675      * @type int
16676      * @private
16677      */
16678     maxX: 0,
16679
16680     /**
16681      * The up constraint
16682      * @property minY
16683      * @type int
16684      * @type int
16685      * @private
16686      */
16687     minY: 0,
16688
16689     /**
16690      * The down constraint
16691      * @property maxY
16692      * @type int
16693      * @private
16694      */
16695     maxY: 0,
16696
16697     /**
16698      * Maintain offsets when we resetconstraints.  Set to true when you want
16699      * the position of the element relative to its parent to stay the same
16700      * when the page changes
16701      *
16702      * @property maintainOffset
16703      * @type boolean
16704      */
16705     maintainOffset: false,
16706
16707     /**
16708      * Array of pixel locations the element will snap to if we specified a
16709      * horizontal graduation/interval.  This array is generated automatically
16710      * when you define a tick interval.
16711      * @property xTicks
16712      * @type int[]
16713      */
16714     xTicks: null,
16715
16716     /**
16717      * Array of pixel locations the element will snap to if we specified a
16718      * vertical graduation/interval.  This array is generated automatically
16719      * when you define a tick interval.
16720      * @property yTicks
16721      * @type int[]
16722      */
16723     yTicks: null,
16724
16725     /**
16726      * By default the drag and drop instance will only respond to the primary
16727      * button click (left button for a right-handed mouse).  Set to true to
16728      * allow drag and drop to start with any mouse click that is propogated
16729      * by the browser
16730      * @property primaryButtonOnly
16731      * @type boolean
16732      */
16733     primaryButtonOnly: true,
16734
16735     /**
16736      * The availabe property is false until the linked dom element is accessible.
16737      * @property available
16738      * @type boolean
16739      */
16740     available: false,
16741
16742     /**
16743      * By default, drags can only be initiated if the mousedown occurs in the
16744      * region the linked element is.  This is done in part to work around a
16745      * bug in some browsers that mis-report the mousedown if the previous
16746      * mouseup happened outside of the window.  This property is set to true
16747      * if outer handles are defined.
16748      *
16749      * @property hasOuterHandles
16750      * @type boolean
16751      * @default false
16752      */
16753     hasOuterHandles: false,
16754
16755     /**
16756      * Code that executes immediately before the startDrag event
16757      * @method b4StartDrag
16758      * @private
16759      */
16760     b4StartDrag: function(x, y) { },
16761
16762     /**
16763      * Abstract method called after a drag/drop object is clicked
16764      * and the drag or mousedown time thresholds have beeen met.
16765      * @method startDrag
16766      * @param {int} X click location
16767      * @param {int} Y click location
16768      */
16769     startDrag: function(x, y) { /* override this */ },
16770
16771     /**
16772      * Code that executes immediately before the onDrag event
16773      * @method b4Drag
16774      * @private
16775      */
16776     b4Drag: function(e) { },
16777
16778     /**
16779      * Abstract method called during the onMouseMove event while dragging an
16780      * object.
16781      * @method onDrag
16782      * @param {Event} e the mousemove event
16783      */
16784     onDrag: function(e) { /* override this */ },
16785
16786     /**
16787      * Abstract method called when this element fist begins hovering over
16788      * another DragDrop obj
16789      * @method onDragEnter
16790      * @param {Event} e the mousemove event
16791      * @param {String|DragDrop[]} id In POINT mode, the element
16792      * id this is hovering over.  In INTERSECT mode, an array of one or more
16793      * dragdrop items being hovered over.
16794      */
16795     onDragEnter: function(e, id) { /* override this */ },
16796
16797     /**
16798      * Code that executes immediately before the onDragOver event
16799      * @method b4DragOver
16800      * @private
16801      */
16802     b4DragOver: function(e) { },
16803
16804     /**
16805      * Abstract method called when this element is hovering over another
16806      * DragDrop obj
16807      * @method onDragOver
16808      * @param {Event} e the mousemove event
16809      * @param {String|DragDrop[]} id In POINT mode, the element
16810      * id this is hovering over.  In INTERSECT mode, an array of dd items
16811      * being hovered over.
16812      */
16813     onDragOver: function(e, id) { /* override this */ },
16814
16815     /**
16816      * Code that executes immediately before the onDragOut event
16817      * @method b4DragOut
16818      * @private
16819      */
16820     b4DragOut: function(e) { },
16821
16822     /**
16823      * Abstract method called when we are no longer hovering over an element
16824      * @method onDragOut
16825      * @param {Event} e the mousemove event
16826      * @param {String|DragDrop[]} id In POINT mode, the element
16827      * id this was hovering over.  In INTERSECT mode, an array of dd items
16828      * that the mouse is no longer over.
16829      */
16830     onDragOut: function(e, id) { /* override this */ },
16831
16832     /**
16833      * Code that executes immediately before the onDragDrop event
16834      * @method b4DragDrop
16835      * @private
16836      */
16837     b4DragDrop: function(e) { },
16838
16839     /**
16840      * Abstract method called when this item is dropped on another DragDrop
16841      * obj
16842      * @method onDragDrop
16843      * @param {Event} e the mouseup event
16844      * @param {String|DragDrop[]} id In POINT mode, the element
16845      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16846      * was dropped on.
16847      */
16848     onDragDrop: function(e, id) { /* override this */ },
16849
16850     /**
16851      * Abstract method called when this item is dropped on an area with no
16852      * drop target
16853      * @method onInvalidDrop
16854      * @param {Event} e the mouseup event
16855      */
16856     onInvalidDrop: function(e) { /* override this */ },
16857
16858     /**
16859      * Code that executes immediately before the endDrag event
16860      * @method b4EndDrag
16861      * @private
16862      */
16863     b4EndDrag: function(e) { },
16864
16865     /**
16866      * Fired when we are done dragging the object
16867      * @method endDrag
16868      * @param {Event} e the mouseup event
16869      */
16870     endDrag: function(e) { /* override this */ },
16871
16872     /**
16873      * Code executed immediately before the onMouseDown event
16874      * @method b4MouseDown
16875      * @param {Event} e the mousedown event
16876      * @private
16877      */
16878     b4MouseDown: function(e) {  },
16879
16880     /**
16881      * Event handler that fires when a drag/drop obj gets a mousedown
16882      * @method onMouseDown
16883      * @param {Event} e the mousedown event
16884      */
16885     onMouseDown: function(e) { /* override this */ },
16886
16887     /**
16888      * Event handler that fires when a drag/drop obj gets a mouseup
16889      * @method onMouseUp
16890      * @param {Event} e the mouseup event
16891      */
16892     onMouseUp: function(e) { /* override this */ },
16893
16894     /**
16895      * Override the onAvailable method to do what is needed after the initial
16896      * position was determined.
16897      * @method onAvailable
16898      */
16899     onAvailable: function () {
16900     },
16901
16902     /*
16903      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16904      * @type Object
16905      */
16906     defaultPadding : {left:0, right:0, top:0, bottom:0},
16907
16908     /*
16909      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16910  *
16911  * Usage:
16912  <pre><code>
16913  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16914                 { dragElId: "existingProxyDiv" });
16915  dd.startDrag = function(){
16916      this.constrainTo("parent-id");
16917  };
16918  </code></pre>
16919  * Or you can initalize it using the {@link Roo.Element} object:
16920  <pre><code>
16921  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16922      startDrag : function(){
16923          this.constrainTo("parent-id");
16924      }
16925  });
16926  </code></pre>
16927      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16928      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16929      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16930      * an object containing the sides to pad. For example: {right:10, bottom:10}
16931      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16932      */
16933     constrainTo : function(constrainTo, pad, inContent){
16934         if(typeof pad == "number"){
16935             pad = {left: pad, right:pad, top:pad, bottom:pad};
16936         }
16937         pad = pad || this.defaultPadding;
16938         var b = Roo.get(this.getEl()).getBox();
16939         var ce = Roo.get(constrainTo);
16940         var s = ce.getScroll();
16941         var c, cd = ce.dom;
16942         if(cd == document.body){
16943             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16944         }else{
16945             xy = ce.getXY();
16946             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16947         }
16948
16949
16950         var topSpace = b.y - c.y;
16951         var leftSpace = b.x - c.x;
16952
16953         this.resetConstraints();
16954         this.setXConstraint(leftSpace - (pad.left||0), // left
16955                 c.width - leftSpace - b.width - (pad.right||0) //right
16956         );
16957         this.setYConstraint(topSpace - (pad.top||0), //top
16958                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16959         );
16960     },
16961
16962     /**
16963      * Returns a reference to the linked element
16964      * @method getEl
16965      * @return {HTMLElement} the html element
16966      */
16967     getEl: function() {
16968         if (!this._domRef) {
16969             this._domRef = Roo.getDom(this.id);
16970         }
16971
16972         return this._domRef;
16973     },
16974
16975     /**
16976      * Returns a reference to the actual element to drag.  By default this is
16977      * the same as the html element, but it can be assigned to another
16978      * element. An example of this can be found in Roo.dd.DDProxy
16979      * @method getDragEl
16980      * @return {HTMLElement} the html element
16981      */
16982     getDragEl: function() {
16983         return Roo.getDom(this.dragElId);
16984     },
16985
16986     /**
16987      * Sets up the DragDrop object.  Must be called in the constructor of any
16988      * Roo.dd.DragDrop subclass
16989      * @method init
16990      * @param id the id of the linked element
16991      * @param {String} sGroup the group of related items
16992      * @param {object} config configuration attributes
16993      */
16994     init: function(id, sGroup, config) {
16995         this.initTarget(id, sGroup, config);
16996         if (!Roo.isTouch) {
16997             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16998         }
16999         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17000         // Event.on(this.id, "selectstart", Event.preventDefault);
17001     },
17002
17003     /**
17004      * Initializes Targeting functionality only... the object does not
17005      * get a mousedown handler.
17006      * @method initTarget
17007      * @param id the id of the linked element
17008      * @param {String} sGroup the group of related items
17009      * @param {object} config configuration attributes
17010      */
17011     initTarget: function(id, sGroup, config) {
17012
17013         // configuration attributes
17014         this.config = config || {};
17015
17016         // create a local reference to the drag and drop manager
17017         this.DDM = Roo.dd.DDM;
17018         // initialize the groups array
17019         this.groups = {};
17020
17021         // assume that we have an element reference instead of an id if the
17022         // parameter is not a string
17023         if (typeof id !== "string") {
17024             id = Roo.id(id);
17025         }
17026
17027         // set the id
17028         this.id = id;
17029
17030         // add to an interaction group
17031         this.addToGroup((sGroup) ? sGroup : "default");
17032
17033         // We don't want to register this as the handle with the manager
17034         // so we just set the id rather than calling the setter.
17035         this.handleElId = id;
17036
17037         // the linked element is the element that gets dragged by default
17038         this.setDragElId(id);
17039
17040         // by default, clicked anchors will not start drag operations.
17041         this.invalidHandleTypes = { A: "A" };
17042         this.invalidHandleIds = {};
17043         this.invalidHandleClasses = [];
17044
17045         this.applyConfig();
17046
17047         this.handleOnAvailable();
17048     },
17049
17050     /**
17051      * Applies the configuration parameters that were passed into the constructor.
17052      * This is supposed to happen at each level through the inheritance chain.  So
17053      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17054      * DragDrop in order to get all of the parameters that are available in
17055      * each object.
17056      * @method applyConfig
17057      */
17058     applyConfig: function() {
17059
17060         // configurable properties:
17061         //    padding, isTarget, maintainOffset, primaryButtonOnly
17062         this.padding           = this.config.padding || [0, 0, 0, 0];
17063         this.isTarget          = (this.config.isTarget !== false);
17064         this.maintainOffset    = (this.config.maintainOffset);
17065         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17066
17067     },
17068
17069     /**
17070      * Executed when the linked element is available
17071      * @method handleOnAvailable
17072      * @private
17073      */
17074     handleOnAvailable: function() {
17075         this.available = true;
17076         this.resetConstraints();
17077         this.onAvailable();
17078     },
17079
17080      /**
17081      * Configures the padding for the target zone in px.  Effectively expands
17082      * (or reduces) the virtual object size for targeting calculations.
17083      * Supports css-style shorthand; if only one parameter is passed, all sides
17084      * will have that padding, and if only two are passed, the top and bottom
17085      * will have the first param, the left and right the second.
17086      * @method setPadding
17087      * @param {int} iTop    Top pad
17088      * @param {int} iRight  Right pad
17089      * @param {int} iBot    Bot pad
17090      * @param {int} iLeft   Left pad
17091      */
17092     setPadding: function(iTop, iRight, iBot, iLeft) {
17093         // this.padding = [iLeft, iRight, iTop, iBot];
17094         if (!iRight && 0 !== iRight) {
17095             this.padding = [iTop, iTop, iTop, iTop];
17096         } else if (!iBot && 0 !== iBot) {
17097             this.padding = [iTop, iRight, iTop, iRight];
17098         } else {
17099             this.padding = [iTop, iRight, iBot, iLeft];
17100         }
17101     },
17102
17103     /**
17104      * Stores the initial placement of the linked element.
17105      * @method setInitialPosition
17106      * @param {int} diffX   the X offset, default 0
17107      * @param {int} diffY   the Y offset, default 0
17108      */
17109     setInitPosition: function(diffX, diffY) {
17110         var el = this.getEl();
17111
17112         if (!this.DDM.verifyEl(el)) {
17113             return;
17114         }
17115
17116         var dx = diffX || 0;
17117         var dy = diffY || 0;
17118
17119         var p = Dom.getXY( el );
17120
17121         this.initPageX = p[0] - dx;
17122         this.initPageY = p[1] - dy;
17123
17124         this.lastPageX = p[0];
17125         this.lastPageY = p[1];
17126
17127
17128         this.setStartPosition(p);
17129     },
17130
17131     /**
17132      * Sets the start position of the element.  This is set when the obj
17133      * is initialized, the reset when a drag is started.
17134      * @method setStartPosition
17135      * @param pos current position (from previous lookup)
17136      * @private
17137      */
17138     setStartPosition: function(pos) {
17139         var p = pos || Dom.getXY( this.getEl() );
17140         this.deltaSetXY = null;
17141
17142         this.startPageX = p[0];
17143         this.startPageY = p[1];
17144     },
17145
17146     /**
17147      * Add this instance to a group of related drag/drop objects.  All
17148      * instances belong to at least one group, and can belong to as many
17149      * groups as needed.
17150      * @method addToGroup
17151      * @param sGroup {string} the name of the group
17152      */
17153     addToGroup: function(sGroup) {
17154         this.groups[sGroup] = true;
17155         this.DDM.regDragDrop(this, sGroup);
17156     },
17157
17158     /**
17159      * Remove's this instance from the supplied interaction group
17160      * @method removeFromGroup
17161      * @param {string}  sGroup  The group to drop
17162      */
17163     removeFromGroup: function(sGroup) {
17164         if (this.groups[sGroup]) {
17165             delete this.groups[sGroup];
17166         }
17167
17168         this.DDM.removeDDFromGroup(this, sGroup);
17169     },
17170
17171     /**
17172      * Allows you to specify that an element other than the linked element
17173      * will be moved with the cursor during a drag
17174      * @method setDragElId
17175      * @param id {string} the id of the element that will be used to initiate the drag
17176      */
17177     setDragElId: function(id) {
17178         this.dragElId = id;
17179     },
17180
17181     /**
17182      * Allows you to specify a child of the linked element that should be
17183      * used to initiate the drag operation.  An example of this would be if
17184      * you have a content div with text and links.  Clicking anywhere in the
17185      * content area would normally start the drag operation.  Use this method
17186      * to specify that an element inside of the content div is the element
17187      * that starts the drag operation.
17188      * @method setHandleElId
17189      * @param id {string} the id of the element that will be used to
17190      * initiate the drag.
17191      */
17192     setHandleElId: function(id) {
17193         if (typeof id !== "string") {
17194             id = Roo.id(id);
17195         }
17196         this.handleElId = id;
17197         this.DDM.regHandle(this.id, id);
17198     },
17199
17200     /**
17201      * Allows you to set an element outside of the linked element as a drag
17202      * handle
17203      * @method setOuterHandleElId
17204      * @param id the id of the element that will be used to initiate the drag
17205      */
17206     setOuterHandleElId: function(id) {
17207         if (typeof id !== "string") {
17208             id = Roo.id(id);
17209         }
17210         Event.on(id, "mousedown",
17211                 this.handleMouseDown, this);
17212         this.setHandleElId(id);
17213
17214         this.hasOuterHandles = true;
17215     },
17216
17217     /**
17218      * Remove all drag and drop hooks for this element
17219      * @method unreg
17220      */
17221     unreg: function() {
17222         Event.un(this.id, "mousedown",
17223                 this.handleMouseDown);
17224         Event.un(this.id, "touchstart",
17225                 this.handleMouseDown);
17226         this._domRef = null;
17227         this.DDM._remove(this);
17228     },
17229
17230     destroy : function(){
17231         this.unreg();
17232     },
17233
17234     /**
17235      * Returns true if this instance is locked, or the drag drop mgr is locked
17236      * (meaning that all drag/drop is disabled on the page.)
17237      * @method isLocked
17238      * @return {boolean} true if this obj or all drag/drop is locked, else
17239      * false
17240      */
17241     isLocked: function() {
17242         return (this.DDM.isLocked() || this.locked);
17243     },
17244
17245     /**
17246      * Fired when this object is clicked
17247      * @method handleMouseDown
17248      * @param {Event} e
17249      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17250      * @private
17251      */
17252     handleMouseDown: function(e, oDD){
17253      
17254         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17255             //Roo.log('not touch/ button !=0');
17256             return;
17257         }
17258         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17259             return; // double touch..
17260         }
17261         
17262
17263         if (this.isLocked()) {
17264             //Roo.log('locked');
17265             return;
17266         }
17267
17268         this.DDM.refreshCache(this.groups);
17269 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17270         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17271         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17272             //Roo.log('no outer handes or not over target');
17273                 // do nothing.
17274         } else {
17275 //            Roo.log('check validator');
17276             if (this.clickValidator(e)) {
17277 //                Roo.log('validate success');
17278                 // set the initial element position
17279                 this.setStartPosition();
17280
17281
17282                 this.b4MouseDown(e);
17283                 this.onMouseDown(e);
17284
17285                 this.DDM.handleMouseDown(e, this);
17286
17287                 this.DDM.stopEvent(e);
17288             } else {
17289
17290
17291             }
17292         }
17293     },
17294
17295     clickValidator: function(e) {
17296         var target = e.getTarget();
17297         return ( this.isValidHandleChild(target) &&
17298                     (this.id == this.handleElId ||
17299                         this.DDM.handleWasClicked(target, this.id)) );
17300     },
17301
17302     /**
17303      * Allows you to specify a tag name that should not start a drag operation
17304      * when clicked.  This is designed to facilitate embedding links within a
17305      * drag handle that do something other than start the drag.
17306      * @method addInvalidHandleType
17307      * @param {string} tagName the type of element to exclude
17308      */
17309     addInvalidHandleType: function(tagName) {
17310         var type = tagName.toUpperCase();
17311         this.invalidHandleTypes[type] = type;
17312     },
17313
17314     /**
17315      * Lets you to specify an element id for a child of a drag handle
17316      * that should not initiate a drag
17317      * @method addInvalidHandleId
17318      * @param {string} id the element id of the element you wish to ignore
17319      */
17320     addInvalidHandleId: function(id) {
17321         if (typeof id !== "string") {
17322             id = Roo.id(id);
17323         }
17324         this.invalidHandleIds[id] = id;
17325     },
17326
17327     /**
17328      * Lets you specify a css class of elements that will not initiate a drag
17329      * @method addInvalidHandleClass
17330      * @param {string} cssClass the class of the elements you wish to ignore
17331      */
17332     addInvalidHandleClass: function(cssClass) {
17333         this.invalidHandleClasses.push(cssClass);
17334     },
17335
17336     /**
17337      * Unsets an excluded tag name set by addInvalidHandleType
17338      * @method removeInvalidHandleType
17339      * @param {string} tagName the type of element to unexclude
17340      */
17341     removeInvalidHandleType: function(tagName) {
17342         var type = tagName.toUpperCase();
17343         // this.invalidHandleTypes[type] = null;
17344         delete this.invalidHandleTypes[type];
17345     },
17346
17347     /**
17348      * Unsets an invalid handle id
17349      * @method removeInvalidHandleId
17350      * @param {string} id the id of the element to re-enable
17351      */
17352     removeInvalidHandleId: function(id) {
17353         if (typeof id !== "string") {
17354             id = Roo.id(id);
17355         }
17356         delete this.invalidHandleIds[id];
17357     },
17358
17359     /**
17360      * Unsets an invalid css class
17361      * @method removeInvalidHandleClass
17362      * @param {string} cssClass the class of the element(s) you wish to
17363      * re-enable
17364      */
17365     removeInvalidHandleClass: function(cssClass) {
17366         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17367             if (this.invalidHandleClasses[i] == cssClass) {
17368                 delete this.invalidHandleClasses[i];
17369             }
17370         }
17371     },
17372
17373     /**
17374      * Checks the tag exclusion list to see if this click should be ignored
17375      * @method isValidHandleChild
17376      * @param {HTMLElement} node the HTMLElement to evaluate
17377      * @return {boolean} true if this is a valid tag type, false if not
17378      */
17379     isValidHandleChild: function(node) {
17380
17381         var valid = true;
17382         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17383         var nodeName;
17384         try {
17385             nodeName = node.nodeName.toUpperCase();
17386         } catch(e) {
17387             nodeName = node.nodeName;
17388         }
17389         valid = valid && !this.invalidHandleTypes[nodeName];
17390         valid = valid && !this.invalidHandleIds[node.id];
17391
17392         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17393             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17394         }
17395
17396
17397         return valid;
17398
17399     },
17400
17401     /**
17402      * Create the array of horizontal tick marks if an interval was specified
17403      * in setXConstraint().
17404      * @method setXTicks
17405      * @private
17406      */
17407     setXTicks: function(iStartX, iTickSize) {
17408         this.xTicks = [];
17409         this.xTickSize = iTickSize;
17410
17411         var tickMap = {};
17412
17413         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17414             if (!tickMap[i]) {
17415                 this.xTicks[this.xTicks.length] = i;
17416                 tickMap[i] = true;
17417             }
17418         }
17419
17420         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17421             if (!tickMap[i]) {
17422                 this.xTicks[this.xTicks.length] = i;
17423                 tickMap[i] = true;
17424             }
17425         }
17426
17427         this.xTicks.sort(this.DDM.numericSort) ;
17428     },
17429
17430     /**
17431      * Create the array of vertical tick marks if an interval was specified in
17432      * setYConstraint().
17433      * @method setYTicks
17434      * @private
17435      */
17436     setYTicks: function(iStartY, iTickSize) {
17437         this.yTicks = [];
17438         this.yTickSize = iTickSize;
17439
17440         var tickMap = {};
17441
17442         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17443             if (!tickMap[i]) {
17444                 this.yTicks[this.yTicks.length] = i;
17445                 tickMap[i] = true;
17446             }
17447         }
17448
17449         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17450             if (!tickMap[i]) {
17451                 this.yTicks[this.yTicks.length] = i;
17452                 tickMap[i] = true;
17453             }
17454         }
17455
17456         this.yTicks.sort(this.DDM.numericSort) ;
17457     },
17458
17459     /**
17460      * By default, the element can be dragged any place on the screen.  Use
17461      * this method to limit the horizontal travel of the element.  Pass in
17462      * 0,0 for the parameters if you want to lock the drag to the y axis.
17463      * @method setXConstraint
17464      * @param {int} iLeft the number of pixels the element can move to the left
17465      * @param {int} iRight the number of pixels the element can move to the
17466      * right
17467      * @param {int} iTickSize optional parameter for specifying that the
17468      * element
17469      * should move iTickSize pixels at a time.
17470      */
17471     setXConstraint: function(iLeft, iRight, iTickSize) {
17472         this.leftConstraint = iLeft;
17473         this.rightConstraint = iRight;
17474
17475         this.minX = this.initPageX - iLeft;
17476         this.maxX = this.initPageX + iRight;
17477         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17478
17479         this.constrainX = true;
17480     },
17481
17482     /**
17483      * Clears any constraints applied to this instance.  Also clears ticks
17484      * since they can't exist independent of a constraint at this time.
17485      * @method clearConstraints
17486      */
17487     clearConstraints: function() {
17488         this.constrainX = false;
17489         this.constrainY = false;
17490         this.clearTicks();
17491     },
17492
17493     /**
17494      * Clears any tick interval defined for this instance
17495      * @method clearTicks
17496      */
17497     clearTicks: function() {
17498         this.xTicks = null;
17499         this.yTicks = null;
17500         this.xTickSize = 0;
17501         this.yTickSize = 0;
17502     },
17503
17504     /**
17505      * By default, the element can be dragged any place on the screen.  Set
17506      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17507      * parameters if you want to lock the drag to the x axis.
17508      * @method setYConstraint
17509      * @param {int} iUp the number of pixels the element can move up
17510      * @param {int} iDown the number of pixels the element can move down
17511      * @param {int} iTickSize optional parameter for specifying that the
17512      * element should move iTickSize pixels at a time.
17513      */
17514     setYConstraint: function(iUp, iDown, iTickSize) {
17515         this.topConstraint = iUp;
17516         this.bottomConstraint = iDown;
17517
17518         this.minY = this.initPageY - iUp;
17519         this.maxY = this.initPageY + iDown;
17520         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17521
17522         this.constrainY = true;
17523
17524     },
17525
17526     /**
17527      * resetConstraints must be called if you manually reposition a dd element.
17528      * @method resetConstraints
17529      * @param {boolean} maintainOffset
17530      */
17531     resetConstraints: function() {
17532
17533
17534         // Maintain offsets if necessary
17535         if (this.initPageX || this.initPageX === 0) {
17536             // figure out how much this thing has moved
17537             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17538             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17539
17540             this.setInitPosition(dx, dy);
17541
17542         // This is the first time we have detected the element's position
17543         } else {
17544             this.setInitPosition();
17545         }
17546
17547         if (this.constrainX) {
17548             this.setXConstraint( this.leftConstraint,
17549                                  this.rightConstraint,
17550                                  this.xTickSize        );
17551         }
17552
17553         if (this.constrainY) {
17554             this.setYConstraint( this.topConstraint,
17555                                  this.bottomConstraint,
17556                                  this.yTickSize         );
17557         }
17558     },
17559
17560     /**
17561      * Normally the drag element is moved pixel by pixel, but we can specify
17562      * that it move a number of pixels at a time.  This method resolves the
17563      * location when we have it set up like this.
17564      * @method getTick
17565      * @param {int} val where we want to place the object
17566      * @param {int[]} tickArray sorted array of valid points
17567      * @return {int} the closest tick
17568      * @private
17569      */
17570     getTick: function(val, tickArray) {
17571
17572         if (!tickArray) {
17573             // If tick interval is not defined, it is effectively 1 pixel,
17574             // so we return the value passed to us.
17575             return val;
17576         } else if (tickArray[0] >= val) {
17577             // The value is lower than the first tick, so we return the first
17578             // tick.
17579             return tickArray[0];
17580         } else {
17581             for (var i=0, len=tickArray.length; i<len; ++i) {
17582                 var next = i + 1;
17583                 if (tickArray[next] && tickArray[next] >= val) {
17584                     var diff1 = val - tickArray[i];
17585                     var diff2 = tickArray[next] - val;
17586                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17587                 }
17588             }
17589
17590             // The value is larger than the last tick, so we return the last
17591             // tick.
17592             return tickArray[tickArray.length - 1];
17593         }
17594     },
17595
17596     /**
17597      * toString method
17598      * @method toString
17599      * @return {string} string representation of the dd obj
17600      */
17601     toString: function() {
17602         return ("DragDrop " + this.id);
17603     }
17604
17605 });
17606
17607 })();
17608 /*
17609  * Based on:
17610  * Ext JS Library 1.1.1
17611  * Copyright(c) 2006-2007, Ext JS, LLC.
17612  *
17613  * Originally Released Under LGPL - original licence link has changed is not relivant.
17614  *
17615  * Fork - LGPL
17616  * <script type="text/javascript">
17617  */
17618
17619
17620 /**
17621  * The drag and drop utility provides a framework for building drag and drop
17622  * applications.  In addition to enabling drag and drop for specific elements,
17623  * the drag and drop elements are tracked by the manager class, and the
17624  * interactions between the various elements are tracked during the drag and
17625  * the implementing code is notified about these important moments.
17626  */
17627
17628 // Only load the library once.  Rewriting the manager class would orphan
17629 // existing drag and drop instances.
17630 if (!Roo.dd.DragDropMgr) {
17631
17632 /**
17633  * @class Roo.dd.DragDropMgr
17634  * DragDropMgr is a singleton that tracks the element interaction for
17635  * all DragDrop items in the window.  Generally, you will not call
17636  * this class directly, but it does have helper methods that could
17637  * be useful in your DragDrop implementations.
17638  * @singleton
17639  */
17640 Roo.dd.DragDropMgr = function() {
17641
17642     var Event = Roo.EventManager;
17643
17644     return {
17645
17646         /**
17647          * Two dimensional Array of registered DragDrop objects.  The first
17648          * dimension is the DragDrop item group, the second the DragDrop
17649          * object.
17650          * @property ids
17651          * @type {string: string}
17652          * @private
17653          * @static
17654          */
17655         ids: {},
17656
17657         /**
17658          * Array of element ids defined as drag handles.  Used to determine
17659          * if the element that generated the mousedown event is actually the
17660          * handle and not the html element itself.
17661          * @property handleIds
17662          * @type {string: string}
17663          * @private
17664          * @static
17665          */
17666         handleIds: {},
17667
17668         /**
17669          * the DragDrop object that is currently being dragged
17670          * @property dragCurrent
17671          * @type DragDrop
17672          * @private
17673          * @static
17674          **/
17675         dragCurrent: null,
17676
17677         /**
17678          * the DragDrop object(s) that are being hovered over
17679          * @property dragOvers
17680          * @type Array
17681          * @private
17682          * @static
17683          */
17684         dragOvers: {},
17685
17686         /**
17687          * the X distance between the cursor and the object being dragged
17688          * @property deltaX
17689          * @type int
17690          * @private
17691          * @static
17692          */
17693         deltaX: 0,
17694
17695         /**
17696          * the Y distance between the cursor and the object being dragged
17697          * @property deltaY
17698          * @type int
17699          * @private
17700          * @static
17701          */
17702         deltaY: 0,
17703
17704         /**
17705          * Flag to determine if we should prevent the default behavior of the
17706          * events we define. By default this is true, but this can be set to
17707          * false if you need the default behavior (not recommended)
17708          * @property preventDefault
17709          * @type boolean
17710          * @static
17711          */
17712         preventDefault: true,
17713
17714         /**
17715          * Flag to determine if we should stop the propagation of the events
17716          * we generate. This is true by default but you may want to set it to
17717          * false if the html element contains other features that require the
17718          * mouse click.
17719          * @property stopPropagation
17720          * @type boolean
17721          * @static
17722          */
17723         stopPropagation: true,
17724
17725         /**
17726          * Internal flag that is set to true when drag and drop has been
17727          * intialized
17728          * @property initialized
17729          * @private
17730          * @static
17731          */
17732         initalized: false,
17733
17734         /**
17735          * All drag and drop can be disabled.
17736          * @property locked
17737          * @private
17738          * @static
17739          */
17740         locked: false,
17741
17742         /**
17743          * Called the first time an element is registered.
17744          * @method init
17745          * @private
17746          * @static
17747          */
17748         init: function() {
17749             this.initialized = true;
17750         },
17751
17752         /**
17753          * In point mode, drag and drop interaction is defined by the
17754          * location of the cursor during the drag/drop
17755          * @property POINT
17756          * @type int
17757          * @static
17758          */
17759         POINT: 0,
17760
17761         /**
17762          * In intersect mode, drag and drop interactio nis defined by the
17763          * overlap of two or more drag and drop objects.
17764          * @property INTERSECT
17765          * @type int
17766          * @static
17767          */
17768         INTERSECT: 1,
17769
17770         /**
17771          * The current drag and drop mode.  Default: POINT
17772          * @property mode
17773          * @type int
17774          * @static
17775          */
17776         mode: 0,
17777
17778         /**
17779          * Runs method on all drag and drop objects
17780          * @method _execOnAll
17781          * @private
17782          * @static
17783          */
17784         _execOnAll: function(sMethod, args) {
17785             for (var i in this.ids) {
17786                 for (var j in this.ids[i]) {
17787                     var oDD = this.ids[i][j];
17788                     if (! this.isTypeOfDD(oDD)) {
17789                         continue;
17790                     }
17791                     oDD[sMethod].apply(oDD, args);
17792                 }
17793             }
17794         },
17795
17796         /**
17797          * Drag and drop initialization.  Sets up the global event handlers
17798          * @method _onLoad
17799          * @private
17800          * @static
17801          */
17802         _onLoad: function() {
17803
17804             this.init();
17805
17806             if (!Roo.isTouch) {
17807                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17808                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17809             }
17810             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17811             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17812             
17813             Event.on(window,   "unload",    this._onUnload, this, true);
17814             Event.on(window,   "resize",    this._onResize, this, true);
17815             // Event.on(window,   "mouseout",    this._test);
17816
17817         },
17818
17819         /**
17820          * Reset constraints on all drag and drop objs
17821          * @method _onResize
17822          * @private
17823          * @static
17824          */
17825         _onResize: function(e) {
17826             this._execOnAll("resetConstraints", []);
17827         },
17828
17829         /**
17830          * Lock all drag and drop functionality
17831          * @method lock
17832          * @static
17833          */
17834         lock: function() { this.locked = true; },
17835
17836         /**
17837          * Unlock all drag and drop functionality
17838          * @method unlock
17839          * @static
17840          */
17841         unlock: function() { this.locked = false; },
17842
17843         /**
17844          * Is drag and drop locked?
17845          * @method isLocked
17846          * @return {boolean} True if drag and drop is locked, false otherwise.
17847          * @static
17848          */
17849         isLocked: function() { return this.locked; },
17850
17851         /**
17852          * Location cache that is set for all drag drop objects when a drag is
17853          * initiated, cleared when the drag is finished.
17854          * @property locationCache
17855          * @private
17856          * @static
17857          */
17858         locationCache: {},
17859
17860         /**
17861          * Set useCache to false if you want to force object the lookup of each
17862          * drag and drop linked element constantly during a drag.
17863          * @property useCache
17864          * @type boolean
17865          * @static
17866          */
17867         useCache: true,
17868
17869         /**
17870          * The number of pixels that the mouse needs to move after the
17871          * mousedown before the drag is initiated.  Default=3;
17872          * @property clickPixelThresh
17873          * @type int
17874          * @static
17875          */
17876         clickPixelThresh: 3,
17877
17878         /**
17879          * The number of milliseconds after the mousedown event to initiate the
17880          * drag if we don't get a mouseup event. Default=1000
17881          * @property clickTimeThresh
17882          * @type int
17883          * @static
17884          */
17885         clickTimeThresh: 350,
17886
17887         /**
17888          * Flag that indicates that either the drag pixel threshold or the
17889          * mousdown time threshold has been met
17890          * @property dragThreshMet
17891          * @type boolean
17892          * @private
17893          * @static
17894          */
17895         dragThreshMet: false,
17896
17897         /**
17898          * Timeout used for the click time threshold
17899          * @property clickTimeout
17900          * @type Object
17901          * @private
17902          * @static
17903          */
17904         clickTimeout: null,
17905
17906         /**
17907          * The X position of the mousedown event stored for later use when a
17908          * drag threshold is met.
17909          * @property startX
17910          * @type int
17911          * @private
17912          * @static
17913          */
17914         startX: 0,
17915
17916         /**
17917          * The Y position of the mousedown event stored for later use when a
17918          * drag threshold is met.
17919          * @property startY
17920          * @type int
17921          * @private
17922          * @static
17923          */
17924         startY: 0,
17925
17926         /**
17927          * Each DragDrop instance must be registered with the DragDropMgr.
17928          * This is executed in DragDrop.init()
17929          * @method regDragDrop
17930          * @param {DragDrop} oDD the DragDrop object to register
17931          * @param {String} sGroup the name of the group this element belongs to
17932          * @static
17933          */
17934         regDragDrop: function(oDD, sGroup) {
17935             if (!this.initialized) { this.init(); }
17936
17937             if (!this.ids[sGroup]) {
17938                 this.ids[sGroup] = {};
17939             }
17940             this.ids[sGroup][oDD.id] = oDD;
17941         },
17942
17943         /**
17944          * Removes the supplied dd instance from the supplied group. Executed
17945          * by DragDrop.removeFromGroup, so don't call this function directly.
17946          * @method removeDDFromGroup
17947          * @private
17948          * @static
17949          */
17950         removeDDFromGroup: function(oDD, sGroup) {
17951             if (!this.ids[sGroup]) {
17952                 this.ids[sGroup] = {};
17953             }
17954
17955             var obj = this.ids[sGroup];
17956             if (obj && obj[oDD.id]) {
17957                 delete obj[oDD.id];
17958             }
17959         },
17960
17961         /**
17962          * Unregisters a drag and drop item.  This is executed in
17963          * DragDrop.unreg, use that method instead of calling this directly.
17964          * @method _remove
17965          * @private
17966          * @static
17967          */
17968         _remove: function(oDD) {
17969             for (var g in oDD.groups) {
17970                 if (g && this.ids[g][oDD.id]) {
17971                     delete this.ids[g][oDD.id];
17972                 }
17973             }
17974             delete this.handleIds[oDD.id];
17975         },
17976
17977         /**
17978          * Each DragDrop handle element must be registered.  This is done
17979          * automatically when executing DragDrop.setHandleElId()
17980          * @method regHandle
17981          * @param {String} sDDId the DragDrop id this element is a handle for
17982          * @param {String} sHandleId the id of the element that is the drag
17983          * handle
17984          * @static
17985          */
17986         regHandle: function(sDDId, sHandleId) {
17987             if (!this.handleIds[sDDId]) {
17988                 this.handleIds[sDDId] = {};
17989             }
17990             this.handleIds[sDDId][sHandleId] = sHandleId;
17991         },
17992
17993         /**
17994          * Utility function to determine if a given element has been
17995          * registered as a drag drop item.
17996          * @method isDragDrop
17997          * @param {String} id the element id to check
17998          * @return {boolean} true if this element is a DragDrop item,
17999          * false otherwise
18000          * @static
18001          */
18002         isDragDrop: function(id) {
18003             return ( this.getDDById(id) ) ? true : false;
18004         },
18005
18006         /**
18007          * Returns the drag and drop instances that are in all groups the
18008          * passed in instance belongs to.
18009          * @method getRelated
18010          * @param {DragDrop} p_oDD the obj to get related data for
18011          * @param {boolean} bTargetsOnly if true, only return targetable objs
18012          * @return {DragDrop[]} the related instances
18013          * @static
18014          */
18015         getRelated: function(p_oDD, bTargetsOnly) {
18016             var oDDs = [];
18017             for (var i in p_oDD.groups) {
18018                 for (j in this.ids[i]) {
18019                     var dd = this.ids[i][j];
18020                     if (! this.isTypeOfDD(dd)) {
18021                         continue;
18022                     }
18023                     if (!bTargetsOnly || dd.isTarget) {
18024                         oDDs[oDDs.length] = dd;
18025                     }
18026                 }
18027             }
18028
18029             return oDDs;
18030         },
18031
18032         /**
18033          * Returns true if the specified dd target is a legal target for
18034          * the specifice drag obj
18035          * @method isLegalTarget
18036          * @param {DragDrop} the drag obj
18037          * @param {DragDrop} the target
18038          * @return {boolean} true if the target is a legal target for the
18039          * dd obj
18040          * @static
18041          */
18042         isLegalTarget: function (oDD, oTargetDD) {
18043             var targets = this.getRelated(oDD, true);
18044             for (var i=0, len=targets.length;i<len;++i) {
18045                 if (targets[i].id == oTargetDD.id) {
18046                     return true;
18047                 }
18048             }
18049
18050             return false;
18051         },
18052
18053         /**
18054          * My goal is to be able to transparently determine if an object is
18055          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18056          * returns "object", oDD.constructor.toString() always returns
18057          * "DragDrop" and not the name of the subclass.  So for now it just
18058          * evaluates a well-known variable in DragDrop.
18059          * @method isTypeOfDD
18060          * @param {Object} the object to evaluate
18061          * @return {boolean} true if typeof oDD = DragDrop
18062          * @static
18063          */
18064         isTypeOfDD: function (oDD) {
18065             return (oDD && oDD.__ygDragDrop);
18066         },
18067
18068         /**
18069          * Utility function to determine if a given element has been
18070          * registered as a drag drop handle for the given Drag Drop object.
18071          * @method isHandle
18072          * @param {String} id the element id to check
18073          * @return {boolean} true if this element is a DragDrop handle, false
18074          * otherwise
18075          * @static
18076          */
18077         isHandle: function(sDDId, sHandleId) {
18078             return ( this.handleIds[sDDId] &&
18079                             this.handleIds[sDDId][sHandleId] );
18080         },
18081
18082         /**
18083          * Returns the DragDrop instance for a given id
18084          * @method getDDById
18085          * @param {String} id the id of the DragDrop object
18086          * @return {DragDrop} the drag drop object, null if it is not found
18087          * @static
18088          */
18089         getDDById: function(id) {
18090             for (var i in this.ids) {
18091                 if (this.ids[i][id]) {
18092                     return this.ids[i][id];
18093                 }
18094             }
18095             return null;
18096         },
18097
18098         /**
18099          * Fired after a registered DragDrop object gets the mousedown event.
18100          * Sets up the events required to track the object being dragged
18101          * @method handleMouseDown
18102          * @param {Event} e the event
18103          * @param oDD the DragDrop object being dragged
18104          * @private
18105          * @static
18106          */
18107         handleMouseDown: function(e, oDD) {
18108             if(Roo.QuickTips){
18109                 Roo.QuickTips.disable();
18110             }
18111             this.currentTarget = e.getTarget();
18112
18113             this.dragCurrent = oDD;
18114
18115             var el = oDD.getEl();
18116
18117             // track start position
18118             this.startX = e.getPageX();
18119             this.startY = e.getPageY();
18120
18121             this.deltaX = this.startX - el.offsetLeft;
18122             this.deltaY = this.startY - el.offsetTop;
18123
18124             this.dragThreshMet = false;
18125
18126             this.clickTimeout = setTimeout(
18127                     function() {
18128                         var DDM = Roo.dd.DDM;
18129                         DDM.startDrag(DDM.startX, DDM.startY);
18130                     },
18131                     this.clickTimeThresh );
18132         },
18133
18134         /**
18135          * Fired when either the drag pixel threshol or the mousedown hold
18136          * time threshold has been met.
18137          * @method startDrag
18138          * @param x {int} the X position of the original mousedown
18139          * @param y {int} the Y position of the original mousedown
18140          * @static
18141          */
18142         startDrag: function(x, y) {
18143             clearTimeout(this.clickTimeout);
18144             if (this.dragCurrent) {
18145                 this.dragCurrent.b4StartDrag(x, y);
18146                 this.dragCurrent.startDrag(x, y);
18147             }
18148             this.dragThreshMet = true;
18149         },
18150
18151         /**
18152          * Internal function to handle the mouseup event.  Will be invoked
18153          * from the context of the document.
18154          * @method handleMouseUp
18155          * @param {Event} e the event
18156          * @private
18157          * @static
18158          */
18159         handleMouseUp: function(e) {
18160
18161             if(Roo.QuickTips){
18162                 Roo.QuickTips.enable();
18163             }
18164             if (! this.dragCurrent) {
18165                 return;
18166             }
18167
18168             clearTimeout(this.clickTimeout);
18169
18170             if (this.dragThreshMet) {
18171                 this.fireEvents(e, true);
18172             } else {
18173             }
18174
18175             this.stopDrag(e);
18176
18177             this.stopEvent(e);
18178         },
18179
18180         /**
18181          * Utility to stop event propagation and event default, if these
18182          * features are turned on.
18183          * @method stopEvent
18184          * @param {Event} e the event as returned by this.getEvent()
18185          * @static
18186          */
18187         stopEvent: function(e){
18188             if(this.stopPropagation) {
18189                 e.stopPropagation();
18190             }
18191
18192             if (this.preventDefault) {
18193                 e.preventDefault();
18194             }
18195         },
18196
18197         /**
18198          * Internal function to clean up event handlers after the drag
18199          * operation is complete
18200          * @method stopDrag
18201          * @param {Event} e the event
18202          * @private
18203          * @static
18204          */
18205         stopDrag: function(e) {
18206             // Fire the drag end event for the item that was dragged
18207             if (this.dragCurrent) {
18208                 if (this.dragThreshMet) {
18209                     this.dragCurrent.b4EndDrag(e);
18210                     this.dragCurrent.endDrag(e);
18211                 }
18212
18213                 this.dragCurrent.onMouseUp(e);
18214             }
18215
18216             this.dragCurrent = null;
18217             this.dragOvers = {};
18218         },
18219
18220         /**
18221          * Internal function to handle the mousemove event.  Will be invoked
18222          * from the context of the html element.
18223          *
18224          * @TODO figure out what we can do about mouse events lost when the
18225          * user drags objects beyond the window boundary.  Currently we can
18226          * detect this in internet explorer by verifying that the mouse is
18227          * down during the mousemove event.  Firefox doesn't give us the
18228          * button state on the mousemove event.
18229          * @method handleMouseMove
18230          * @param {Event} e the event
18231          * @private
18232          * @static
18233          */
18234         handleMouseMove: function(e) {
18235             if (! this.dragCurrent) {
18236                 return true;
18237             }
18238
18239             // var button = e.which || e.button;
18240
18241             // check for IE mouseup outside of page boundary
18242             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18243                 this.stopEvent(e);
18244                 return this.handleMouseUp(e);
18245             }
18246
18247             if (!this.dragThreshMet) {
18248                 var diffX = Math.abs(this.startX - e.getPageX());
18249                 var diffY = Math.abs(this.startY - e.getPageY());
18250                 if (diffX > this.clickPixelThresh ||
18251                             diffY > this.clickPixelThresh) {
18252                     this.startDrag(this.startX, this.startY);
18253                 }
18254             }
18255
18256             if (this.dragThreshMet) {
18257                 this.dragCurrent.b4Drag(e);
18258                 this.dragCurrent.onDrag(e);
18259                 if(!this.dragCurrent.moveOnly){
18260                     this.fireEvents(e, false);
18261                 }
18262             }
18263
18264             this.stopEvent(e);
18265
18266             return true;
18267         },
18268
18269         /**
18270          * Iterates over all of the DragDrop elements to find ones we are
18271          * hovering over or dropping on
18272          * @method fireEvents
18273          * @param {Event} e the event
18274          * @param {boolean} isDrop is this a drop op or a mouseover op?
18275          * @private
18276          * @static
18277          */
18278         fireEvents: function(e, isDrop) {
18279             var dc = this.dragCurrent;
18280
18281             // If the user did the mouse up outside of the window, we could
18282             // get here even though we have ended the drag.
18283             if (!dc || dc.isLocked()) {
18284                 return;
18285             }
18286
18287             var pt = e.getPoint();
18288
18289             // cache the previous dragOver array
18290             var oldOvers = [];
18291
18292             var outEvts   = [];
18293             var overEvts  = [];
18294             var dropEvts  = [];
18295             var enterEvts = [];
18296
18297             // Check to see if the object(s) we were hovering over is no longer
18298             // being hovered over so we can fire the onDragOut event
18299             for (var i in this.dragOvers) {
18300
18301                 var ddo = this.dragOvers[i];
18302
18303                 if (! this.isTypeOfDD(ddo)) {
18304                     continue;
18305                 }
18306
18307                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18308                     outEvts.push( ddo );
18309                 }
18310
18311                 oldOvers[i] = true;
18312                 delete this.dragOvers[i];
18313             }
18314
18315             for (var sGroup in dc.groups) {
18316
18317                 if ("string" != typeof sGroup) {
18318                     continue;
18319                 }
18320
18321                 for (i in this.ids[sGroup]) {
18322                     var oDD = this.ids[sGroup][i];
18323                     if (! this.isTypeOfDD(oDD)) {
18324                         continue;
18325                     }
18326
18327                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18328                         if (this.isOverTarget(pt, oDD, this.mode)) {
18329                             // look for drop interactions
18330                             if (isDrop) {
18331                                 dropEvts.push( oDD );
18332                             // look for drag enter and drag over interactions
18333                             } else {
18334
18335                                 // initial drag over: dragEnter fires
18336                                 if (!oldOvers[oDD.id]) {
18337                                     enterEvts.push( oDD );
18338                                 // subsequent drag overs: dragOver fires
18339                                 } else {
18340                                     overEvts.push( oDD );
18341                                 }
18342
18343                                 this.dragOvers[oDD.id] = oDD;
18344                             }
18345                         }
18346                     }
18347                 }
18348             }
18349
18350             if (this.mode) {
18351                 if (outEvts.length) {
18352                     dc.b4DragOut(e, outEvts);
18353                     dc.onDragOut(e, outEvts);
18354                 }
18355
18356                 if (enterEvts.length) {
18357                     dc.onDragEnter(e, enterEvts);
18358                 }
18359
18360                 if (overEvts.length) {
18361                     dc.b4DragOver(e, overEvts);
18362                     dc.onDragOver(e, overEvts);
18363                 }
18364
18365                 if (dropEvts.length) {
18366                     dc.b4DragDrop(e, dropEvts);
18367                     dc.onDragDrop(e, dropEvts);
18368                 }
18369
18370             } else {
18371                 // fire dragout events
18372                 var len = 0;
18373                 for (i=0, len=outEvts.length; i<len; ++i) {
18374                     dc.b4DragOut(e, outEvts[i].id);
18375                     dc.onDragOut(e, outEvts[i].id);
18376                 }
18377
18378                 // fire enter events
18379                 for (i=0,len=enterEvts.length; i<len; ++i) {
18380                     // dc.b4DragEnter(e, oDD.id);
18381                     dc.onDragEnter(e, enterEvts[i].id);
18382                 }
18383
18384                 // fire over events
18385                 for (i=0,len=overEvts.length; i<len; ++i) {
18386                     dc.b4DragOver(e, overEvts[i].id);
18387                     dc.onDragOver(e, overEvts[i].id);
18388                 }
18389
18390                 // fire drop events
18391                 for (i=0, len=dropEvts.length; i<len; ++i) {
18392                     dc.b4DragDrop(e, dropEvts[i].id);
18393                     dc.onDragDrop(e, dropEvts[i].id);
18394                 }
18395
18396             }
18397
18398             // notify about a drop that did not find a target
18399             if (isDrop && !dropEvts.length) {
18400                 dc.onInvalidDrop(e);
18401             }
18402
18403         },
18404
18405         /**
18406          * Helper function for getting the best match from the list of drag
18407          * and drop objects returned by the drag and drop events when we are
18408          * in INTERSECT mode.  It returns either the first object that the
18409          * cursor is over, or the object that has the greatest overlap with
18410          * the dragged element.
18411          * @method getBestMatch
18412          * @param  {DragDrop[]} dds The array of drag and drop objects
18413          * targeted
18414          * @return {DragDrop}       The best single match
18415          * @static
18416          */
18417         getBestMatch: function(dds) {
18418             var winner = null;
18419             // Return null if the input is not what we expect
18420             //if (!dds || !dds.length || dds.length == 0) {
18421                // winner = null;
18422             // If there is only one item, it wins
18423             //} else if (dds.length == 1) {
18424
18425             var len = dds.length;
18426
18427             if (len == 1) {
18428                 winner = dds[0];
18429             } else {
18430                 // Loop through the targeted items
18431                 for (var i=0; i<len; ++i) {
18432                     var dd = dds[i];
18433                     // If the cursor is over the object, it wins.  If the
18434                     // cursor is over multiple matches, the first one we come
18435                     // to wins.
18436                     if (dd.cursorIsOver) {
18437                         winner = dd;
18438                         break;
18439                     // Otherwise the object with the most overlap wins
18440                     } else {
18441                         if (!winner ||
18442                             winner.overlap.getArea() < dd.overlap.getArea()) {
18443                             winner = dd;
18444                         }
18445                     }
18446                 }
18447             }
18448
18449             return winner;
18450         },
18451
18452         /**
18453          * Refreshes the cache of the top-left and bottom-right points of the
18454          * drag and drop objects in the specified group(s).  This is in the
18455          * format that is stored in the drag and drop instance, so typical
18456          * usage is:
18457          * <code>
18458          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18459          * </code>
18460          * Alternatively:
18461          * <code>
18462          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18463          * </code>
18464          * @TODO this really should be an indexed array.  Alternatively this
18465          * method could accept both.
18466          * @method refreshCache
18467          * @param {Object} groups an associative array of groups to refresh
18468          * @static
18469          */
18470         refreshCache: function(groups) {
18471             for (var sGroup in groups) {
18472                 if ("string" != typeof sGroup) {
18473                     continue;
18474                 }
18475                 for (var i in this.ids[sGroup]) {
18476                     var oDD = this.ids[sGroup][i];
18477
18478                     if (this.isTypeOfDD(oDD)) {
18479                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18480                         var loc = this.getLocation(oDD);
18481                         if (loc) {
18482                             this.locationCache[oDD.id] = loc;
18483                         } else {
18484                             delete this.locationCache[oDD.id];
18485                             // this will unregister the drag and drop object if
18486                             // the element is not in a usable state
18487                             // oDD.unreg();
18488                         }
18489                     }
18490                 }
18491             }
18492         },
18493
18494         /**
18495          * This checks to make sure an element exists and is in the DOM.  The
18496          * main purpose is to handle cases where innerHTML is used to remove
18497          * drag and drop objects from the DOM.  IE provides an 'unspecified
18498          * error' when trying to access the offsetParent of such an element
18499          * @method verifyEl
18500          * @param {HTMLElement} el the element to check
18501          * @return {boolean} true if the element looks usable
18502          * @static
18503          */
18504         verifyEl: function(el) {
18505             if (el) {
18506                 var parent;
18507                 if(Roo.isIE){
18508                     try{
18509                         parent = el.offsetParent;
18510                     }catch(e){}
18511                 }else{
18512                     parent = el.offsetParent;
18513                 }
18514                 if (parent) {
18515                     return true;
18516                 }
18517             }
18518
18519             return false;
18520         },
18521
18522         /**
18523          * Returns a Region object containing the drag and drop element's position
18524          * and size, including the padding configured for it
18525          * @method getLocation
18526          * @param {DragDrop} oDD the drag and drop object to get the
18527          *                       location for
18528          * @return {Roo.lib.Region} a Region object representing the total area
18529          *                             the element occupies, including any padding
18530          *                             the instance is configured for.
18531          * @static
18532          */
18533         getLocation: function(oDD) {
18534             if (! this.isTypeOfDD(oDD)) {
18535                 return null;
18536             }
18537
18538             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18539
18540             try {
18541                 pos= Roo.lib.Dom.getXY(el);
18542             } catch (e) { }
18543
18544             if (!pos) {
18545                 return null;
18546             }
18547
18548             x1 = pos[0];
18549             x2 = x1 + el.offsetWidth;
18550             y1 = pos[1];
18551             y2 = y1 + el.offsetHeight;
18552
18553             t = y1 - oDD.padding[0];
18554             r = x2 + oDD.padding[1];
18555             b = y2 + oDD.padding[2];
18556             l = x1 - oDD.padding[3];
18557
18558             return new Roo.lib.Region( t, r, b, l );
18559         },
18560
18561         /**
18562          * Checks the cursor location to see if it over the target
18563          * @method isOverTarget
18564          * @param {Roo.lib.Point} pt The point to evaluate
18565          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18566          * @return {boolean} true if the mouse is over the target
18567          * @private
18568          * @static
18569          */
18570         isOverTarget: function(pt, oTarget, intersect) {
18571             // use cache if available
18572             var loc = this.locationCache[oTarget.id];
18573             if (!loc || !this.useCache) {
18574                 loc = this.getLocation(oTarget);
18575                 this.locationCache[oTarget.id] = loc;
18576
18577             }
18578
18579             if (!loc) {
18580                 return false;
18581             }
18582
18583             oTarget.cursorIsOver = loc.contains( pt );
18584
18585             // DragDrop is using this as a sanity check for the initial mousedown
18586             // in this case we are done.  In POINT mode, if the drag obj has no
18587             // contraints, we are also done. Otherwise we need to evaluate the
18588             // location of the target as related to the actual location of the
18589             // dragged element.
18590             var dc = this.dragCurrent;
18591             if (!dc || !dc.getTargetCoord ||
18592                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18593                 return oTarget.cursorIsOver;
18594             }
18595
18596             oTarget.overlap = null;
18597
18598             // Get the current location of the drag element, this is the
18599             // location of the mouse event less the delta that represents
18600             // where the original mousedown happened on the element.  We
18601             // need to consider constraints and ticks as well.
18602             var pos = dc.getTargetCoord(pt.x, pt.y);
18603
18604             var el = dc.getDragEl();
18605             var curRegion = new Roo.lib.Region( pos.y,
18606                                                    pos.x + el.offsetWidth,
18607                                                    pos.y + el.offsetHeight,
18608                                                    pos.x );
18609
18610             var overlap = curRegion.intersect(loc);
18611
18612             if (overlap) {
18613                 oTarget.overlap = overlap;
18614                 return (intersect) ? true : oTarget.cursorIsOver;
18615             } else {
18616                 return false;
18617             }
18618         },
18619
18620         /**
18621          * unload event handler
18622          * @method _onUnload
18623          * @private
18624          * @static
18625          */
18626         _onUnload: function(e, me) {
18627             Roo.dd.DragDropMgr.unregAll();
18628         },
18629
18630         /**
18631          * Cleans up the drag and drop events and objects.
18632          * @method unregAll
18633          * @private
18634          * @static
18635          */
18636         unregAll: function() {
18637
18638             if (this.dragCurrent) {
18639                 this.stopDrag();
18640                 this.dragCurrent = null;
18641             }
18642
18643             this._execOnAll("unreg", []);
18644
18645             for (i in this.elementCache) {
18646                 delete this.elementCache[i];
18647             }
18648
18649             this.elementCache = {};
18650             this.ids = {};
18651         },
18652
18653         /**
18654          * A cache of DOM elements
18655          * @property elementCache
18656          * @private
18657          * @static
18658          */
18659         elementCache: {},
18660
18661         /**
18662          * Get the wrapper for the DOM element specified
18663          * @method getElWrapper
18664          * @param {String} id the id of the element to get
18665          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18666          * @private
18667          * @deprecated This wrapper isn't that useful
18668          * @static
18669          */
18670         getElWrapper: function(id) {
18671             var oWrapper = this.elementCache[id];
18672             if (!oWrapper || !oWrapper.el) {
18673                 oWrapper = this.elementCache[id] =
18674                     new this.ElementWrapper(Roo.getDom(id));
18675             }
18676             return oWrapper;
18677         },
18678
18679         /**
18680          * Returns the actual DOM element
18681          * @method getElement
18682          * @param {String} id the id of the elment to get
18683          * @return {Object} The element
18684          * @deprecated use Roo.getDom instead
18685          * @static
18686          */
18687         getElement: function(id) {
18688             return Roo.getDom(id);
18689         },
18690
18691         /**
18692          * Returns the style property for the DOM element (i.e.,
18693          * document.getElById(id).style)
18694          * @method getCss
18695          * @param {String} id the id of the elment to get
18696          * @return {Object} The style property of the element
18697          * @deprecated use Roo.getDom instead
18698          * @static
18699          */
18700         getCss: function(id) {
18701             var el = Roo.getDom(id);
18702             return (el) ? el.style : null;
18703         },
18704
18705         /**
18706          * Inner class for cached elements
18707          * @class DragDropMgr.ElementWrapper
18708          * @for DragDropMgr
18709          * @private
18710          * @deprecated
18711          */
18712         ElementWrapper: function(el) {
18713                 /**
18714                  * The element
18715                  * @property el
18716                  */
18717                 this.el = el || null;
18718                 /**
18719                  * The element id
18720                  * @property id
18721                  */
18722                 this.id = this.el && el.id;
18723                 /**
18724                  * A reference to the style property
18725                  * @property css
18726                  */
18727                 this.css = this.el && el.style;
18728             },
18729
18730         /**
18731          * Returns the X position of an html element
18732          * @method getPosX
18733          * @param el the element for which to get the position
18734          * @return {int} the X coordinate
18735          * @for DragDropMgr
18736          * @deprecated use Roo.lib.Dom.getX instead
18737          * @static
18738          */
18739         getPosX: function(el) {
18740             return Roo.lib.Dom.getX(el);
18741         },
18742
18743         /**
18744          * Returns the Y position of an html element
18745          * @method getPosY
18746          * @param el the element for which to get the position
18747          * @return {int} the Y coordinate
18748          * @deprecated use Roo.lib.Dom.getY instead
18749          * @static
18750          */
18751         getPosY: function(el) {
18752             return Roo.lib.Dom.getY(el);
18753         },
18754
18755         /**
18756          * Swap two nodes.  In IE, we use the native method, for others we
18757          * emulate the IE behavior
18758          * @method swapNode
18759          * @param n1 the first node to swap
18760          * @param n2 the other node to swap
18761          * @static
18762          */
18763         swapNode: function(n1, n2) {
18764             if (n1.swapNode) {
18765                 n1.swapNode(n2);
18766             } else {
18767                 var p = n2.parentNode;
18768                 var s = n2.nextSibling;
18769
18770                 if (s == n1) {
18771                     p.insertBefore(n1, n2);
18772                 } else if (n2 == n1.nextSibling) {
18773                     p.insertBefore(n2, n1);
18774                 } else {
18775                     n1.parentNode.replaceChild(n2, n1);
18776                     p.insertBefore(n1, s);
18777                 }
18778             }
18779         },
18780
18781         /**
18782          * Returns the current scroll position
18783          * @method getScroll
18784          * @private
18785          * @static
18786          */
18787         getScroll: function () {
18788             var t, l, dde=document.documentElement, db=document.body;
18789             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18790                 t = dde.scrollTop;
18791                 l = dde.scrollLeft;
18792             } else if (db) {
18793                 t = db.scrollTop;
18794                 l = db.scrollLeft;
18795             } else {
18796
18797             }
18798             return { top: t, left: l };
18799         },
18800
18801         /**
18802          * Returns the specified element style property
18803          * @method getStyle
18804          * @param {HTMLElement} el          the element
18805          * @param {string}      styleProp   the style property
18806          * @return {string} The value of the style property
18807          * @deprecated use Roo.lib.Dom.getStyle
18808          * @static
18809          */
18810         getStyle: function(el, styleProp) {
18811             return Roo.fly(el).getStyle(styleProp);
18812         },
18813
18814         /**
18815          * Gets the scrollTop
18816          * @method getScrollTop
18817          * @return {int} the document's scrollTop
18818          * @static
18819          */
18820         getScrollTop: function () { return this.getScroll().top; },
18821
18822         /**
18823          * Gets the scrollLeft
18824          * @method getScrollLeft
18825          * @return {int} the document's scrollTop
18826          * @static
18827          */
18828         getScrollLeft: function () { return this.getScroll().left; },
18829
18830         /**
18831          * Sets the x/y position of an element to the location of the
18832          * target element.
18833          * @method moveToEl
18834          * @param {HTMLElement} moveEl      The element to move
18835          * @param {HTMLElement} targetEl    The position reference element
18836          * @static
18837          */
18838         moveToEl: function (moveEl, targetEl) {
18839             var aCoord = Roo.lib.Dom.getXY(targetEl);
18840             Roo.lib.Dom.setXY(moveEl, aCoord);
18841         },
18842
18843         /**
18844          * Numeric array sort function
18845          * @method numericSort
18846          * @static
18847          */
18848         numericSort: function(a, b) { return (a - b); },
18849
18850         /**
18851          * Internal counter
18852          * @property _timeoutCount
18853          * @private
18854          * @static
18855          */
18856         _timeoutCount: 0,
18857
18858         /**
18859          * Trying to make the load order less important.  Without this we get
18860          * an error if this file is loaded before the Event Utility.
18861          * @method _addListeners
18862          * @private
18863          * @static
18864          */
18865         _addListeners: function() {
18866             var DDM = Roo.dd.DDM;
18867             if ( Roo.lib.Event && document ) {
18868                 DDM._onLoad();
18869             } else {
18870                 if (DDM._timeoutCount > 2000) {
18871                 } else {
18872                     setTimeout(DDM._addListeners, 10);
18873                     if (document && document.body) {
18874                         DDM._timeoutCount += 1;
18875                     }
18876                 }
18877             }
18878         },
18879
18880         /**
18881          * Recursively searches the immediate parent and all child nodes for
18882          * the handle element in order to determine wheter or not it was
18883          * clicked.
18884          * @method handleWasClicked
18885          * @param node the html element to inspect
18886          * @static
18887          */
18888         handleWasClicked: function(node, id) {
18889             if (this.isHandle(id, node.id)) {
18890                 return true;
18891             } else {
18892                 // check to see if this is a text node child of the one we want
18893                 var p = node.parentNode;
18894
18895                 while (p) {
18896                     if (this.isHandle(id, p.id)) {
18897                         return true;
18898                     } else {
18899                         p = p.parentNode;
18900                     }
18901                 }
18902             }
18903
18904             return false;
18905         }
18906
18907     };
18908
18909 }();
18910
18911 // shorter alias, save a few bytes
18912 Roo.dd.DDM = Roo.dd.DragDropMgr;
18913 Roo.dd.DDM._addListeners();
18914
18915 }/*
18916  * Based on:
18917  * Ext JS Library 1.1.1
18918  * Copyright(c) 2006-2007, Ext JS, LLC.
18919  *
18920  * Originally Released Under LGPL - original licence link has changed is not relivant.
18921  *
18922  * Fork - LGPL
18923  * <script type="text/javascript">
18924  */
18925
18926 /**
18927  * @class Roo.dd.DD
18928  * A DragDrop implementation where the linked element follows the
18929  * mouse cursor during a drag.
18930  * @extends Roo.dd.DragDrop
18931  * @constructor
18932  * @param {String} id the id of the linked element
18933  * @param {String} sGroup the group of related DragDrop items
18934  * @param {object} config an object containing configurable attributes
18935  *                Valid properties for DD:
18936  *                    scroll
18937  */
18938 Roo.dd.DD = function(id, sGroup, config) {
18939     if (id) {
18940         this.init(id, sGroup, config);
18941     }
18942 };
18943
18944 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18945
18946     /**
18947      * When set to true, the utility automatically tries to scroll the browser
18948      * window wehn a drag and drop element is dragged near the viewport boundary.
18949      * Defaults to true.
18950      * @property scroll
18951      * @type boolean
18952      */
18953     scroll: true,
18954
18955     /**
18956      * Sets the pointer offset to the distance between the linked element's top
18957      * left corner and the location the element was clicked
18958      * @method autoOffset
18959      * @param {int} iPageX the X coordinate of the click
18960      * @param {int} iPageY the Y coordinate of the click
18961      */
18962     autoOffset: function(iPageX, iPageY) {
18963         var x = iPageX - this.startPageX;
18964         var y = iPageY - this.startPageY;
18965         this.setDelta(x, y);
18966     },
18967
18968     /**
18969      * Sets the pointer offset.  You can call this directly to force the
18970      * offset to be in a particular location (e.g., pass in 0,0 to set it
18971      * to the center of the object)
18972      * @method setDelta
18973      * @param {int} iDeltaX the distance from the left
18974      * @param {int} iDeltaY the distance from the top
18975      */
18976     setDelta: function(iDeltaX, iDeltaY) {
18977         this.deltaX = iDeltaX;
18978         this.deltaY = iDeltaY;
18979     },
18980
18981     /**
18982      * Sets the drag element to the location of the mousedown or click event,
18983      * maintaining the cursor location relative to the location on the element
18984      * that was clicked.  Override this if you want to place the element in a
18985      * location other than where the cursor is.
18986      * @method setDragElPos
18987      * @param {int} iPageX the X coordinate of the mousedown or drag event
18988      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18989      */
18990     setDragElPos: function(iPageX, iPageY) {
18991         // the first time we do this, we are going to check to make sure
18992         // the element has css positioning
18993
18994         var el = this.getDragEl();
18995         this.alignElWithMouse(el, iPageX, iPageY);
18996     },
18997
18998     /**
18999      * Sets the element to the location of the mousedown or click event,
19000      * maintaining the cursor location relative to the location on the element
19001      * that was clicked.  Override this if you want to place the element in a
19002      * location other than where the cursor is.
19003      * @method alignElWithMouse
19004      * @param {HTMLElement} el the element to move
19005      * @param {int} iPageX the X coordinate of the mousedown or drag event
19006      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19007      */
19008     alignElWithMouse: function(el, iPageX, iPageY) {
19009         var oCoord = this.getTargetCoord(iPageX, iPageY);
19010         var fly = el.dom ? el : Roo.fly(el);
19011         if (!this.deltaSetXY) {
19012             var aCoord = [oCoord.x, oCoord.y];
19013             fly.setXY(aCoord);
19014             var newLeft = fly.getLeft(true);
19015             var newTop  = fly.getTop(true);
19016             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19017         } else {
19018             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19019         }
19020
19021         this.cachePosition(oCoord.x, oCoord.y);
19022         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19023         return oCoord;
19024     },
19025
19026     /**
19027      * Saves the most recent position so that we can reset the constraints and
19028      * tick marks on-demand.  We need to know this so that we can calculate the
19029      * number of pixels the element is offset from its original position.
19030      * @method cachePosition
19031      * @param iPageX the current x position (optional, this just makes it so we
19032      * don't have to look it up again)
19033      * @param iPageY the current y position (optional, this just makes it so we
19034      * don't have to look it up again)
19035      */
19036     cachePosition: function(iPageX, iPageY) {
19037         if (iPageX) {
19038             this.lastPageX = iPageX;
19039             this.lastPageY = iPageY;
19040         } else {
19041             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19042             this.lastPageX = aCoord[0];
19043             this.lastPageY = aCoord[1];
19044         }
19045     },
19046
19047     /**
19048      * Auto-scroll the window if the dragged object has been moved beyond the
19049      * visible window boundary.
19050      * @method autoScroll
19051      * @param {int} x the drag element's x position
19052      * @param {int} y the drag element's y position
19053      * @param {int} h the height of the drag element
19054      * @param {int} w the width of the drag element
19055      * @private
19056      */
19057     autoScroll: function(x, y, h, w) {
19058
19059         if (this.scroll) {
19060             // The client height
19061             var clientH = Roo.lib.Dom.getViewWidth();
19062
19063             // The client width
19064             var clientW = Roo.lib.Dom.getViewHeight();
19065
19066             // The amt scrolled down
19067             var st = this.DDM.getScrollTop();
19068
19069             // The amt scrolled right
19070             var sl = this.DDM.getScrollLeft();
19071
19072             // Location of the bottom of the element
19073             var bot = h + y;
19074
19075             // Location of the right of the element
19076             var right = w + x;
19077
19078             // The distance from the cursor to the bottom of the visible area,
19079             // adjusted so that we don't scroll if the cursor is beyond the
19080             // element drag constraints
19081             var toBot = (clientH + st - y - this.deltaY);
19082
19083             // The distance from the cursor to the right of the visible area
19084             var toRight = (clientW + sl - x - this.deltaX);
19085
19086
19087             // How close to the edge the cursor must be before we scroll
19088             // var thresh = (document.all) ? 100 : 40;
19089             var thresh = 40;
19090
19091             // How many pixels to scroll per autoscroll op.  This helps to reduce
19092             // clunky scrolling. IE is more sensitive about this ... it needs this
19093             // value to be higher.
19094             var scrAmt = (document.all) ? 80 : 30;
19095
19096             // Scroll down if we are near the bottom of the visible page and the
19097             // obj extends below the crease
19098             if ( bot > clientH && toBot < thresh ) {
19099                 window.scrollTo(sl, st + scrAmt);
19100             }
19101
19102             // Scroll up if the window is scrolled down and the top of the object
19103             // goes above the top border
19104             if ( y < st && st > 0 && y - st < thresh ) {
19105                 window.scrollTo(sl, st - scrAmt);
19106             }
19107
19108             // Scroll right if the obj is beyond the right border and the cursor is
19109             // near the border.
19110             if ( right > clientW && toRight < thresh ) {
19111                 window.scrollTo(sl + scrAmt, st);
19112             }
19113
19114             // Scroll left if the window has been scrolled to the right and the obj
19115             // extends past the left border
19116             if ( x < sl && sl > 0 && x - sl < thresh ) {
19117                 window.scrollTo(sl - scrAmt, st);
19118             }
19119         }
19120     },
19121
19122     /**
19123      * Finds the location the element should be placed if we want to move
19124      * it to where the mouse location less the click offset would place us.
19125      * @method getTargetCoord
19126      * @param {int} iPageX the X coordinate of the click
19127      * @param {int} iPageY the Y coordinate of the click
19128      * @return an object that contains the coordinates (Object.x and Object.y)
19129      * @private
19130      */
19131     getTargetCoord: function(iPageX, iPageY) {
19132
19133
19134         var x = iPageX - this.deltaX;
19135         var y = iPageY - this.deltaY;
19136
19137         if (this.constrainX) {
19138             if (x < this.minX) { x = this.minX; }
19139             if (x > this.maxX) { x = this.maxX; }
19140         }
19141
19142         if (this.constrainY) {
19143             if (y < this.minY) { y = this.minY; }
19144             if (y > this.maxY) { y = this.maxY; }
19145         }
19146
19147         x = this.getTick(x, this.xTicks);
19148         y = this.getTick(y, this.yTicks);
19149
19150
19151         return {x:x, y:y};
19152     },
19153
19154     /*
19155      * Sets up config options specific to this class. Overrides
19156      * Roo.dd.DragDrop, but all versions of this method through the
19157      * inheritance chain are called
19158      */
19159     applyConfig: function() {
19160         Roo.dd.DD.superclass.applyConfig.call(this);
19161         this.scroll = (this.config.scroll !== false);
19162     },
19163
19164     /*
19165      * Event that fires prior to the onMouseDown event.  Overrides
19166      * Roo.dd.DragDrop.
19167      */
19168     b4MouseDown: function(e) {
19169         // this.resetConstraints();
19170         this.autoOffset(e.getPageX(),
19171                             e.getPageY());
19172     },
19173
19174     /*
19175      * Event that fires prior to the onDrag event.  Overrides
19176      * Roo.dd.DragDrop.
19177      */
19178     b4Drag: function(e) {
19179         this.setDragElPos(e.getPageX(),
19180                             e.getPageY());
19181     },
19182
19183     toString: function() {
19184         return ("DD " + this.id);
19185     }
19186
19187     //////////////////////////////////////////////////////////////////////////
19188     // Debugging ygDragDrop events that can be overridden
19189     //////////////////////////////////////////////////////////////////////////
19190     /*
19191     startDrag: function(x, y) {
19192     },
19193
19194     onDrag: function(e) {
19195     },
19196
19197     onDragEnter: function(e, id) {
19198     },
19199
19200     onDragOver: function(e, id) {
19201     },
19202
19203     onDragOut: function(e, id) {
19204     },
19205
19206     onDragDrop: function(e, id) {
19207     },
19208
19209     endDrag: function(e) {
19210     }
19211
19212     */
19213
19214 });/*
19215  * Based on:
19216  * Ext JS Library 1.1.1
19217  * Copyright(c) 2006-2007, Ext JS, LLC.
19218  *
19219  * Originally Released Under LGPL - original licence link has changed is not relivant.
19220  *
19221  * Fork - LGPL
19222  * <script type="text/javascript">
19223  */
19224
19225 /**
19226  * @class Roo.dd.DDProxy
19227  * A DragDrop implementation that inserts an empty, bordered div into
19228  * the document that follows the cursor during drag operations.  At the time of
19229  * the click, the frame div is resized to the dimensions of the linked html
19230  * element, and moved to the exact location of the linked element.
19231  *
19232  * References to the "frame" element refer to the single proxy element that
19233  * was created to be dragged in place of all DDProxy elements on the
19234  * page.
19235  *
19236  * @extends Roo.dd.DD
19237  * @constructor
19238  * @param {String} id the id of the linked html element
19239  * @param {String} sGroup the group of related DragDrop objects
19240  * @param {object} config an object containing configurable attributes
19241  *                Valid properties for DDProxy in addition to those in DragDrop:
19242  *                   resizeFrame, centerFrame, dragElId
19243  */
19244 Roo.dd.DDProxy = function(id, sGroup, config) {
19245     if (id) {
19246         this.init(id, sGroup, config);
19247         this.initFrame();
19248     }
19249 };
19250
19251 /**
19252  * The default drag frame div id
19253  * @property Roo.dd.DDProxy.dragElId
19254  * @type String
19255  * @static
19256  */
19257 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19258
19259 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19260
19261     /**
19262      * By default we resize the drag frame to be the same size as the element
19263      * we want to drag (this is to get the frame effect).  We can turn it off
19264      * if we want a different behavior.
19265      * @property resizeFrame
19266      * @type boolean
19267      */
19268     resizeFrame: true,
19269
19270     /**
19271      * By default the frame is positioned exactly where the drag element is, so
19272      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19273      * you do not have constraints on the obj is to have the drag frame centered
19274      * around the cursor.  Set centerFrame to true for this effect.
19275      * @property centerFrame
19276      * @type boolean
19277      */
19278     centerFrame: false,
19279
19280     /**
19281      * Creates the proxy element if it does not yet exist
19282      * @method createFrame
19283      */
19284     createFrame: function() {
19285         var self = this;
19286         var body = document.body;
19287
19288         if (!body || !body.firstChild) {
19289             setTimeout( function() { self.createFrame(); }, 50 );
19290             return;
19291         }
19292
19293         var div = this.getDragEl();
19294
19295         if (!div) {
19296             div    = document.createElement("div");
19297             div.id = this.dragElId;
19298             var s  = div.style;
19299
19300             s.position   = "absolute";
19301             s.visibility = "hidden";
19302             s.cursor     = "move";
19303             s.border     = "2px solid #aaa";
19304             s.zIndex     = 999;
19305
19306             // appendChild can blow up IE if invoked prior to the window load event
19307             // while rendering a table.  It is possible there are other scenarios
19308             // that would cause this to happen as well.
19309             body.insertBefore(div, body.firstChild);
19310         }
19311     },
19312
19313     /**
19314      * Initialization for the drag frame element.  Must be called in the
19315      * constructor of all subclasses
19316      * @method initFrame
19317      */
19318     initFrame: function() {
19319         this.createFrame();
19320     },
19321
19322     applyConfig: function() {
19323         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19324
19325         this.resizeFrame = (this.config.resizeFrame !== false);
19326         this.centerFrame = (this.config.centerFrame);
19327         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19328     },
19329
19330     /**
19331      * Resizes the drag frame to the dimensions of the clicked object, positions
19332      * it over the object, and finally displays it
19333      * @method showFrame
19334      * @param {int} iPageX X click position
19335      * @param {int} iPageY Y click position
19336      * @private
19337      */
19338     showFrame: function(iPageX, iPageY) {
19339         var el = this.getEl();
19340         var dragEl = this.getDragEl();
19341         var s = dragEl.style;
19342
19343         this._resizeProxy();
19344
19345         if (this.centerFrame) {
19346             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19347                            Math.round(parseInt(s.height, 10)/2) );
19348         }
19349
19350         this.setDragElPos(iPageX, iPageY);
19351
19352         Roo.fly(dragEl).show();
19353     },
19354
19355     /**
19356      * The proxy is automatically resized to the dimensions of the linked
19357      * element when a drag is initiated, unless resizeFrame is set to false
19358      * @method _resizeProxy
19359      * @private
19360      */
19361     _resizeProxy: function() {
19362         if (this.resizeFrame) {
19363             var el = this.getEl();
19364             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19365         }
19366     },
19367
19368     // overrides Roo.dd.DragDrop
19369     b4MouseDown: function(e) {
19370         var x = e.getPageX();
19371         var y = e.getPageY();
19372         this.autoOffset(x, y);
19373         this.setDragElPos(x, y);
19374     },
19375
19376     // overrides Roo.dd.DragDrop
19377     b4StartDrag: function(x, y) {
19378         // show the drag frame
19379         this.showFrame(x, y);
19380     },
19381
19382     // overrides Roo.dd.DragDrop
19383     b4EndDrag: function(e) {
19384         Roo.fly(this.getDragEl()).hide();
19385     },
19386
19387     // overrides Roo.dd.DragDrop
19388     // By default we try to move the element to the last location of the frame.
19389     // This is so that the default behavior mirrors that of Roo.dd.DD.
19390     endDrag: function(e) {
19391
19392         var lel = this.getEl();
19393         var del = this.getDragEl();
19394
19395         // Show the drag frame briefly so we can get its position
19396         del.style.visibility = "";
19397
19398         this.beforeMove();
19399         // Hide the linked element before the move to get around a Safari
19400         // rendering bug.
19401         lel.style.visibility = "hidden";
19402         Roo.dd.DDM.moveToEl(lel, del);
19403         del.style.visibility = "hidden";
19404         lel.style.visibility = "";
19405
19406         this.afterDrag();
19407     },
19408
19409     beforeMove : function(){
19410
19411     },
19412
19413     afterDrag : function(){
19414
19415     },
19416
19417     toString: function() {
19418         return ("DDProxy " + this.id);
19419     }
19420
19421 });
19422 /*
19423  * Based on:
19424  * Ext JS Library 1.1.1
19425  * Copyright(c) 2006-2007, Ext JS, LLC.
19426  *
19427  * Originally Released Under LGPL - original licence link has changed is not relivant.
19428  *
19429  * Fork - LGPL
19430  * <script type="text/javascript">
19431  */
19432
19433  /**
19434  * @class Roo.dd.DDTarget
19435  * A DragDrop implementation that does not move, but can be a drop
19436  * target.  You would get the same result by simply omitting implementation
19437  * for the event callbacks, but this way we reduce the processing cost of the
19438  * event listener and the callbacks.
19439  * @extends Roo.dd.DragDrop
19440  * @constructor
19441  * @param {String} id the id of the element that is a drop target
19442  * @param {String} sGroup the group of related DragDrop objects
19443  * @param {object} config an object containing configurable attributes
19444  *                 Valid properties for DDTarget in addition to those in
19445  *                 DragDrop:
19446  *                    none
19447  */
19448 Roo.dd.DDTarget = function(id, sGroup, config) {
19449     if (id) {
19450         this.initTarget(id, sGroup, config);
19451     }
19452     if (config.listeners || config.events) { 
19453        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19454             listeners : config.listeners || {}, 
19455             events : config.events || {} 
19456         });    
19457     }
19458 };
19459
19460 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19461 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19462     toString: function() {
19463         return ("DDTarget " + this.id);
19464     }
19465 });
19466 /*
19467  * Based on:
19468  * Ext JS Library 1.1.1
19469  * Copyright(c) 2006-2007, Ext JS, LLC.
19470  *
19471  * Originally Released Under LGPL - original licence link has changed is not relivant.
19472  *
19473  * Fork - LGPL
19474  * <script type="text/javascript">
19475  */
19476  
19477
19478 /**
19479  * @class Roo.dd.ScrollManager
19480  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19481  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19482  * @singleton
19483  */
19484 Roo.dd.ScrollManager = function(){
19485     var ddm = Roo.dd.DragDropMgr;
19486     var els = {};
19487     var dragEl = null;
19488     var proc = {};
19489     
19490     
19491     
19492     var onStop = function(e){
19493         dragEl = null;
19494         clearProc();
19495     };
19496     
19497     var triggerRefresh = function(){
19498         if(ddm.dragCurrent){
19499              ddm.refreshCache(ddm.dragCurrent.groups);
19500         }
19501     };
19502     
19503     var doScroll = function(){
19504         if(ddm.dragCurrent){
19505             var dds = Roo.dd.ScrollManager;
19506             if(!dds.animate){
19507                 if(proc.el.scroll(proc.dir, dds.increment)){
19508                     triggerRefresh();
19509                 }
19510             }else{
19511                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19512             }
19513         }
19514     };
19515     
19516     var clearProc = function(){
19517         if(proc.id){
19518             clearInterval(proc.id);
19519         }
19520         proc.id = 0;
19521         proc.el = null;
19522         proc.dir = "";
19523     };
19524     
19525     var startProc = function(el, dir){
19526          Roo.log('scroll startproc');
19527         clearProc();
19528         proc.el = el;
19529         proc.dir = dir;
19530         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19531     };
19532     
19533     var onFire = function(e, isDrop){
19534        
19535         if(isDrop || !ddm.dragCurrent){ return; }
19536         var dds = Roo.dd.ScrollManager;
19537         if(!dragEl || dragEl != ddm.dragCurrent){
19538             dragEl = ddm.dragCurrent;
19539             // refresh regions on drag start
19540             dds.refreshCache();
19541         }
19542         
19543         var xy = Roo.lib.Event.getXY(e);
19544         var pt = new Roo.lib.Point(xy[0], xy[1]);
19545         for(var id in els){
19546             var el = els[id], r = el._region;
19547             if(r && r.contains(pt) && el.isScrollable()){
19548                 if(r.bottom - pt.y <= dds.thresh){
19549                     if(proc.el != el){
19550                         startProc(el, "down");
19551                     }
19552                     return;
19553                 }else if(r.right - pt.x <= dds.thresh){
19554                     if(proc.el != el){
19555                         startProc(el, "left");
19556                     }
19557                     return;
19558                 }else if(pt.y - r.top <= dds.thresh){
19559                     if(proc.el != el){
19560                         startProc(el, "up");
19561                     }
19562                     return;
19563                 }else if(pt.x - r.left <= dds.thresh){
19564                     if(proc.el != el){
19565                         startProc(el, "right");
19566                     }
19567                     return;
19568                 }
19569             }
19570         }
19571         clearProc();
19572     };
19573     
19574     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19575     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19576     
19577     return {
19578         /**
19579          * Registers new overflow element(s) to auto scroll
19580          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19581          */
19582         register : function(el){
19583             if(el instanceof Array){
19584                 for(var i = 0, len = el.length; i < len; i++) {
19585                         this.register(el[i]);
19586                 }
19587             }else{
19588                 el = Roo.get(el);
19589                 els[el.id] = el;
19590             }
19591             Roo.dd.ScrollManager.els = els;
19592         },
19593         
19594         /**
19595          * Unregisters overflow element(s) so they are no longer scrolled
19596          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19597          */
19598         unregister : function(el){
19599             if(el instanceof Array){
19600                 for(var i = 0, len = el.length; i < len; i++) {
19601                         this.unregister(el[i]);
19602                 }
19603             }else{
19604                 el = Roo.get(el);
19605                 delete els[el.id];
19606             }
19607         },
19608         
19609         /**
19610          * The number of pixels from the edge of a container the pointer needs to be to 
19611          * trigger scrolling (defaults to 25)
19612          * @type Number
19613          */
19614         thresh : 25,
19615         
19616         /**
19617          * The number of pixels to scroll in each scroll increment (defaults to 50)
19618          * @type Number
19619          */
19620         increment : 100,
19621         
19622         /**
19623          * The frequency of scrolls in milliseconds (defaults to 500)
19624          * @type Number
19625          */
19626         frequency : 500,
19627         
19628         /**
19629          * True to animate the scroll (defaults to true)
19630          * @type Boolean
19631          */
19632         animate: true,
19633         
19634         /**
19635          * The animation duration in seconds - 
19636          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19637          * @type Number
19638          */
19639         animDuration: .4,
19640         
19641         /**
19642          * Manually trigger a cache refresh.
19643          */
19644         refreshCache : function(){
19645             for(var id in els){
19646                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19647                     els[id]._region = els[id].getRegion();
19648                 }
19649             }
19650         }
19651     };
19652 }();/*
19653  * Based on:
19654  * Ext JS Library 1.1.1
19655  * Copyright(c) 2006-2007, Ext JS, LLC.
19656  *
19657  * Originally Released Under LGPL - original licence link has changed is not relivant.
19658  *
19659  * Fork - LGPL
19660  * <script type="text/javascript">
19661  */
19662  
19663
19664 /**
19665  * @class Roo.dd.Registry
19666  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19667  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19668  * @singleton
19669  */
19670 Roo.dd.Registry = function(){
19671     var elements = {}; 
19672     var handles = {}; 
19673     var autoIdSeed = 0;
19674
19675     var getId = function(el, autogen){
19676         if(typeof el == "string"){
19677             return el;
19678         }
19679         var id = el.id;
19680         if(!id && autogen !== false){
19681             id = "roodd-" + (++autoIdSeed);
19682             el.id = id;
19683         }
19684         return id;
19685     };
19686     
19687     return {
19688     /**
19689      * Register a drag drop element
19690      * @param {String|HTMLElement} element The id or DOM node to register
19691      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19692      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19693      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19694      * populated in the data object (if applicable):
19695      * <pre>
19696 Value      Description<br />
19697 ---------  ------------------------------------------<br />
19698 handles    Array of DOM nodes that trigger dragging<br />
19699            for the element being registered<br />
19700 isHandle   True if the element passed in triggers<br />
19701            dragging itself, else false
19702 </pre>
19703      */
19704         register : function(el, data){
19705             data = data || {};
19706             if(typeof el == "string"){
19707                 el = document.getElementById(el);
19708             }
19709             data.ddel = el;
19710             elements[getId(el)] = data;
19711             if(data.isHandle !== false){
19712                 handles[data.ddel.id] = data;
19713             }
19714             if(data.handles){
19715                 var hs = data.handles;
19716                 for(var i = 0, len = hs.length; i < len; i++){
19717                         handles[getId(hs[i])] = data;
19718                 }
19719             }
19720         },
19721
19722     /**
19723      * Unregister a drag drop element
19724      * @param {String|HTMLElement}  element The id or DOM node to unregister
19725      */
19726         unregister : function(el){
19727             var id = getId(el, false);
19728             var data = elements[id];
19729             if(data){
19730                 delete elements[id];
19731                 if(data.handles){
19732                     var hs = data.handles;
19733                     for(var i = 0, len = hs.length; i < len; i++){
19734                         delete handles[getId(hs[i], false)];
19735                     }
19736                 }
19737             }
19738         },
19739
19740     /**
19741      * Returns the handle registered for a DOM Node by id
19742      * @param {String|HTMLElement} id The DOM node or id to look up
19743      * @return {Object} handle The custom handle data
19744      */
19745         getHandle : function(id){
19746             if(typeof id != "string"){ // must be element?
19747                 id = id.id;
19748             }
19749             return handles[id];
19750         },
19751
19752     /**
19753      * Returns the handle that is registered for the DOM node that is the target of the event
19754      * @param {Event} e The event
19755      * @return {Object} handle The custom handle data
19756      */
19757         getHandleFromEvent : function(e){
19758             var t = Roo.lib.Event.getTarget(e);
19759             return t ? handles[t.id] : null;
19760         },
19761
19762     /**
19763      * Returns a custom data object that is registered for a DOM node by id
19764      * @param {String|HTMLElement} id The DOM node or id to look up
19765      * @return {Object} data The custom data
19766      */
19767         getTarget : function(id){
19768             if(typeof id != "string"){ // must be element?
19769                 id = id.id;
19770             }
19771             return elements[id];
19772         },
19773
19774     /**
19775      * Returns a custom data object that is registered for the DOM node that is the target of the event
19776      * @param {Event} e The event
19777      * @return {Object} data The custom data
19778      */
19779         getTargetFromEvent : function(e){
19780             var t = Roo.lib.Event.getTarget(e);
19781             return t ? elements[t.id] || handles[t.id] : null;
19782         }
19783     };
19784 }();/*
19785  * Based on:
19786  * Ext JS Library 1.1.1
19787  * Copyright(c) 2006-2007, Ext JS, LLC.
19788  *
19789  * Originally Released Under LGPL - original licence link has changed is not relivant.
19790  *
19791  * Fork - LGPL
19792  * <script type="text/javascript">
19793  */
19794  
19795
19796 /**
19797  * @class Roo.dd.StatusProxy
19798  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19799  * default drag proxy used by all Roo.dd components.
19800  * @constructor
19801  * @param {Object} config
19802  */
19803 Roo.dd.StatusProxy = function(config){
19804     Roo.apply(this, config);
19805     this.id = this.id || Roo.id();
19806     this.el = new Roo.Layer({
19807         dh: {
19808             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19809                 {tag: "div", cls: "x-dd-drop-icon"},
19810                 {tag: "div", cls: "x-dd-drag-ghost"}
19811             ]
19812         }, 
19813         shadow: !config || config.shadow !== false
19814     });
19815     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19816     this.dropStatus = this.dropNotAllowed;
19817 };
19818
19819 Roo.dd.StatusProxy.prototype = {
19820     /**
19821      * @cfg {String} dropAllowed
19822      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19823      */
19824     dropAllowed : "x-dd-drop-ok",
19825     /**
19826      * @cfg {String} dropNotAllowed
19827      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19828      */
19829     dropNotAllowed : "x-dd-drop-nodrop",
19830
19831     /**
19832      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19833      * over the current target element.
19834      * @param {String} cssClass The css class for the new drop status indicator image
19835      */
19836     setStatus : function(cssClass){
19837         cssClass = cssClass || this.dropNotAllowed;
19838         if(this.dropStatus != cssClass){
19839             this.el.replaceClass(this.dropStatus, cssClass);
19840             this.dropStatus = cssClass;
19841         }
19842     },
19843
19844     /**
19845      * Resets the status indicator to the default dropNotAllowed value
19846      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19847      */
19848     reset : function(clearGhost){
19849         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19850         this.dropStatus = this.dropNotAllowed;
19851         if(clearGhost){
19852             this.ghost.update("");
19853         }
19854     },
19855
19856     /**
19857      * Updates the contents of the ghost element
19858      * @param {String} html The html that will replace the current innerHTML of the ghost element
19859      */
19860     update : function(html){
19861         if(typeof html == "string"){
19862             this.ghost.update(html);
19863         }else{
19864             this.ghost.update("");
19865             html.style.margin = "0";
19866             this.ghost.dom.appendChild(html);
19867         }
19868         // ensure float = none set?? cant remember why though.
19869         var el = this.ghost.dom.firstChild;
19870                 if(el){
19871                         Roo.fly(el).setStyle('float', 'none');
19872                 }
19873     },
19874     
19875     /**
19876      * Returns the underlying proxy {@link Roo.Layer}
19877      * @return {Roo.Layer} el
19878     */
19879     getEl : function(){
19880         return this.el;
19881     },
19882
19883     /**
19884      * Returns the ghost element
19885      * @return {Roo.Element} el
19886      */
19887     getGhost : function(){
19888         return this.ghost;
19889     },
19890
19891     /**
19892      * Hides the proxy
19893      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19894      */
19895     hide : function(clear){
19896         this.el.hide();
19897         if(clear){
19898             this.reset(true);
19899         }
19900     },
19901
19902     /**
19903      * Stops the repair animation if it's currently running
19904      */
19905     stop : function(){
19906         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19907             this.anim.stop();
19908         }
19909     },
19910
19911     /**
19912      * Displays this proxy
19913      */
19914     show : function(){
19915         this.el.show();
19916     },
19917
19918     /**
19919      * Force the Layer to sync its shadow and shim positions to the element
19920      */
19921     sync : function(){
19922         this.el.sync();
19923     },
19924
19925     /**
19926      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19927      * invalid drop operation by the item being dragged.
19928      * @param {Array} xy The XY position of the element ([x, y])
19929      * @param {Function} callback The function to call after the repair is complete
19930      * @param {Object} scope The scope in which to execute the callback
19931      */
19932     repair : function(xy, callback, scope){
19933         this.callback = callback;
19934         this.scope = scope;
19935         if(xy && this.animRepair !== false){
19936             this.el.addClass("x-dd-drag-repair");
19937             this.el.hideUnders(true);
19938             this.anim = this.el.shift({
19939                 duration: this.repairDuration || .5,
19940                 easing: 'easeOut',
19941                 xy: xy,
19942                 stopFx: true,
19943                 callback: this.afterRepair,
19944                 scope: this
19945             });
19946         }else{
19947             this.afterRepair();
19948         }
19949     },
19950
19951     // private
19952     afterRepair : function(){
19953         this.hide(true);
19954         if(typeof this.callback == "function"){
19955             this.callback.call(this.scope || this);
19956         }
19957         this.callback = null;
19958         this.scope = null;
19959     }
19960 };/*
19961  * Based on:
19962  * Ext JS Library 1.1.1
19963  * Copyright(c) 2006-2007, Ext JS, LLC.
19964  *
19965  * Originally Released Under LGPL - original licence link has changed is not relivant.
19966  *
19967  * Fork - LGPL
19968  * <script type="text/javascript">
19969  */
19970
19971 /**
19972  * @class Roo.dd.DragSource
19973  * @extends Roo.dd.DDProxy
19974  * A simple class that provides the basic implementation needed to make any element draggable.
19975  * @constructor
19976  * @param {String/HTMLElement/Element} el The container element
19977  * @param {Object} config
19978  */
19979 Roo.dd.DragSource = function(el, config){
19980     this.el = Roo.get(el);
19981     this.dragData = {};
19982     
19983     Roo.apply(this, config);
19984     
19985     if(!this.proxy){
19986         this.proxy = new Roo.dd.StatusProxy();
19987     }
19988
19989     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19990           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19991     
19992     this.dragging = false;
19993 };
19994
19995 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19996     /**
19997      * @cfg {String} dropAllowed
19998      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19999      */
20000     dropAllowed : "x-dd-drop-ok",
20001     /**
20002      * @cfg {String} dropNotAllowed
20003      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20004      */
20005     dropNotAllowed : "x-dd-drop-nodrop",
20006
20007     /**
20008      * Returns the data object associated with this drag source
20009      * @return {Object} data An object containing arbitrary data
20010      */
20011     getDragData : function(e){
20012         return this.dragData;
20013     },
20014
20015     // private
20016     onDragEnter : function(e, id){
20017         var target = Roo.dd.DragDropMgr.getDDById(id);
20018         this.cachedTarget = target;
20019         if(this.beforeDragEnter(target, e, id) !== false){
20020             if(target.isNotifyTarget){
20021                 var status = target.notifyEnter(this, e, this.dragData);
20022                 this.proxy.setStatus(status);
20023             }else{
20024                 this.proxy.setStatus(this.dropAllowed);
20025             }
20026             
20027             if(this.afterDragEnter){
20028                 /**
20029                  * An empty function by default, but provided so that you can perform a custom action
20030                  * when the dragged item enters the drop target by providing an implementation.
20031                  * @param {Roo.dd.DragDrop} target The drop target
20032                  * @param {Event} e The event object
20033                  * @param {String} id The id of the dragged element
20034                  * @method afterDragEnter
20035                  */
20036                 this.afterDragEnter(target, e, id);
20037             }
20038         }
20039     },
20040
20041     /**
20042      * An empty function by default, but provided so that you can perform a custom action
20043      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20044      * @param {Roo.dd.DragDrop} target The drop target
20045      * @param {Event} e The event object
20046      * @param {String} id The id of the dragged element
20047      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20048      */
20049     beforeDragEnter : function(target, e, id){
20050         return true;
20051     },
20052
20053     // private
20054     alignElWithMouse: function() {
20055         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20056         this.proxy.sync();
20057     },
20058
20059     // private
20060     onDragOver : function(e, id){
20061         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20062         if(this.beforeDragOver(target, e, id) !== false){
20063             if(target.isNotifyTarget){
20064                 var status = target.notifyOver(this, e, this.dragData);
20065                 this.proxy.setStatus(status);
20066             }
20067
20068             if(this.afterDragOver){
20069                 /**
20070                  * An empty function by default, but provided so that you can perform a custom action
20071                  * while the dragged item is over the drop target by providing an implementation.
20072                  * @param {Roo.dd.DragDrop} target The drop target
20073                  * @param {Event} e The event object
20074                  * @param {String} id The id of the dragged element
20075                  * @method afterDragOver
20076                  */
20077                 this.afterDragOver(target, e, id);
20078             }
20079         }
20080     },
20081
20082     /**
20083      * An empty function by default, but provided so that you can perform a custom action
20084      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20085      * @param {Roo.dd.DragDrop} target The drop target
20086      * @param {Event} e The event object
20087      * @param {String} id The id of the dragged element
20088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20089      */
20090     beforeDragOver : function(target, e, id){
20091         return true;
20092     },
20093
20094     // private
20095     onDragOut : function(e, id){
20096         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20097         if(this.beforeDragOut(target, e, id) !== false){
20098             if(target.isNotifyTarget){
20099                 target.notifyOut(this, e, this.dragData);
20100             }
20101             this.proxy.reset();
20102             if(this.afterDragOut){
20103                 /**
20104                  * An empty function by default, but provided so that you can perform a custom action
20105                  * after the dragged item is dragged out of the target without dropping.
20106                  * @param {Roo.dd.DragDrop} target The drop target
20107                  * @param {Event} e The event object
20108                  * @param {String} id The id of the dragged element
20109                  * @method afterDragOut
20110                  */
20111                 this.afterDragOut(target, e, id);
20112             }
20113         }
20114         this.cachedTarget = null;
20115     },
20116
20117     /**
20118      * An empty function by default, but provided so that you can perform a custom action before the dragged
20119      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20120      * @param {Roo.dd.DragDrop} target The drop target
20121      * @param {Event} e The event object
20122      * @param {String} id The id of the dragged element
20123      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20124      */
20125     beforeDragOut : function(target, e, id){
20126         return true;
20127     },
20128     
20129     // private
20130     onDragDrop : function(e, id){
20131         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20132         if(this.beforeDragDrop(target, e, id) !== false){
20133             if(target.isNotifyTarget){
20134                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20135                     this.onValidDrop(target, e, id);
20136                 }else{
20137                     this.onInvalidDrop(target, e, id);
20138                 }
20139             }else{
20140                 this.onValidDrop(target, e, id);
20141             }
20142             
20143             if(this.afterDragDrop){
20144                 /**
20145                  * An empty function by default, but provided so that you can perform a custom action
20146                  * after a valid drag drop has occurred by providing an implementation.
20147                  * @param {Roo.dd.DragDrop} target The drop target
20148                  * @param {Event} e The event object
20149                  * @param {String} id The id of the dropped element
20150                  * @method afterDragDrop
20151                  */
20152                 this.afterDragDrop(target, e, id);
20153             }
20154         }
20155         delete this.cachedTarget;
20156     },
20157
20158     /**
20159      * An empty function by default, but provided so that you can perform a custom action before the dragged
20160      * item is dropped onto the target and optionally cancel the onDragDrop.
20161      * @param {Roo.dd.DragDrop} target The drop target
20162      * @param {Event} e The event object
20163      * @param {String} id The id of the dragged element
20164      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20165      */
20166     beforeDragDrop : function(target, e, id){
20167         return true;
20168     },
20169
20170     // private
20171     onValidDrop : function(target, e, id){
20172         this.hideProxy();
20173         if(this.afterValidDrop){
20174             /**
20175              * An empty function by default, but provided so that you can perform a custom action
20176              * after a valid drop has occurred by providing an implementation.
20177              * @param {Object} target The target DD 
20178              * @param {Event} e The event object
20179              * @param {String} id The id of the dropped element
20180              * @method afterInvalidDrop
20181              */
20182             this.afterValidDrop(target, e, id);
20183         }
20184     },
20185
20186     // private
20187     getRepairXY : function(e, data){
20188         return this.el.getXY();  
20189     },
20190
20191     // private
20192     onInvalidDrop : function(target, e, id){
20193         this.beforeInvalidDrop(target, e, id);
20194         if(this.cachedTarget){
20195             if(this.cachedTarget.isNotifyTarget){
20196                 this.cachedTarget.notifyOut(this, e, this.dragData);
20197             }
20198             this.cacheTarget = null;
20199         }
20200         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20201
20202         if(this.afterInvalidDrop){
20203             /**
20204              * An empty function by default, but provided so that you can perform a custom action
20205              * after an invalid drop has occurred by providing an implementation.
20206              * @param {Event} e The event object
20207              * @param {String} id The id of the dropped element
20208              * @method afterInvalidDrop
20209              */
20210             this.afterInvalidDrop(e, id);
20211         }
20212     },
20213
20214     // private
20215     afterRepair : function(){
20216         if(Roo.enableFx){
20217             this.el.highlight(this.hlColor || "c3daf9");
20218         }
20219         this.dragging = false;
20220     },
20221
20222     /**
20223      * An empty function by default, but provided so that you can perform a custom action after an invalid
20224      * drop has occurred.
20225      * @param {Roo.dd.DragDrop} target The drop target
20226      * @param {Event} e The event object
20227      * @param {String} id The id of the dragged element
20228      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20229      */
20230     beforeInvalidDrop : function(target, e, id){
20231         return true;
20232     },
20233
20234     // private
20235     handleMouseDown : function(e){
20236         if(this.dragging) {
20237             return;
20238         }
20239         var data = this.getDragData(e);
20240         if(data && this.onBeforeDrag(data, e) !== false){
20241             this.dragData = data;
20242             this.proxy.stop();
20243             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20244         } 
20245     },
20246
20247     /**
20248      * An empty function by default, but provided so that you can perform a custom action before the initial
20249      * drag event begins and optionally cancel it.
20250      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20251      * @param {Event} e The event object
20252      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20253      */
20254     onBeforeDrag : function(data, e){
20255         return true;
20256     },
20257
20258     /**
20259      * An empty function by default, but provided so that you can perform a custom action once the initial
20260      * drag event has begun.  The drag cannot be canceled from this function.
20261      * @param {Number} x The x position of the click on the dragged object
20262      * @param {Number} y The y position of the click on the dragged object
20263      */
20264     onStartDrag : Roo.emptyFn,
20265
20266     // private - YUI override
20267     startDrag : function(x, y){
20268         this.proxy.reset();
20269         this.dragging = true;
20270         this.proxy.update("");
20271         this.onInitDrag(x, y);
20272         this.proxy.show();
20273     },
20274
20275     // private
20276     onInitDrag : function(x, y){
20277         var clone = this.el.dom.cloneNode(true);
20278         clone.id = Roo.id(); // prevent duplicate ids
20279         this.proxy.update(clone);
20280         this.onStartDrag(x, y);
20281         return true;
20282     },
20283
20284     /**
20285      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20286      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20287      */
20288     getProxy : function(){
20289         return this.proxy;  
20290     },
20291
20292     /**
20293      * Hides the drag source's {@link Roo.dd.StatusProxy}
20294      */
20295     hideProxy : function(){
20296         this.proxy.hide();  
20297         this.proxy.reset(true);
20298         this.dragging = false;
20299     },
20300
20301     // private
20302     triggerCacheRefresh : function(){
20303         Roo.dd.DDM.refreshCache(this.groups);
20304     },
20305
20306     // private - override to prevent hiding
20307     b4EndDrag: function(e) {
20308     },
20309
20310     // private - override to prevent moving
20311     endDrag : function(e){
20312         this.onEndDrag(this.dragData, e);
20313     },
20314
20315     // private
20316     onEndDrag : function(data, e){
20317     },
20318     
20319     // private - pin to cursor
20320     autoOffset : function(x, y) {
20321         this.setDelta(-12, -20);
20322     }    
20323 });/*
20324  * Based on:
20325  * Ext JS Library 1.1.1
20326  * Copyright(c) 2006-2007, Ext JS, LLC.
20327  *
20328  * Originally Released Under LGPL - original licence link has changed is not relivant.
20329  *
20330  * Fork - LGPL
20331  * <script type="text/javascript">
20332  */
20333
20334
20335 /**
20336  * @class Roo.dd.DropTarget
20337  * @extends Roo.dd.DDTarget
20338  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20339  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20340  * @constructor
20341  * @param {String/HTMLElement/Element} el The container element
20342  * @param {Object} config
20343  */
20344 Roo.dd.DropTarget = function(el, config){
20345     this.el = Roo.get(el);
20346     
20347     var listeners = false; ;
20348     if (config && config.listeners) {
20349         listeners= config.listeners;
20350         delete config.listeners;
20351     }
20352     Roo.apply(this, config);
20353     
20354     if(this.containerScroll){
20355         Roo.dd.ScrollManager.register(this.el);
20356     }
20357     this.addEvents( {
20358          /**
20359          * @scope Roo.dd.DropTarget
20360          */
20361          
20362          /**
20363          * @event enter
20364          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20365          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20366          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20367          * 
20368          * IMPORTANT : it should set this.overClass and this.dropAllowed
20369          * 
20370          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20371          * @param {Event} e The event
20372          * @param {Object} data An object containing arbitrary data supplied by the drag source
20373          */
20374         "enter" : true,
20375         
20376          /**
20377          * @event over
20378          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20379          * This method will be called on every mouse movement while the drag source is over the drop target.
20380          * This default implementation simply returns the dropAllowed config value.
20381          * 
20382          * IMPORTANT : it should set this.dropAllowed
20383          * 
20384          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20385          * @param {Event} e The event
20386          * @param {Object} data An object containing arbitrary data supplied by the drag source
20387          
20388          */
20389         "over" : true,
20390         /**
20391          * @event out
20392          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20393          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20394          * overClass (if any) from the drop element.
20395          * 
20396          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20397          * @param {Event} e The event
20398          * @param {Object} data An object containing arbitrary data supplied by the drag source
20399          */
20400          "out" : true,
20401          
20402         /**
20403          * @event drop
20404          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20405          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20406          * implementation that does something to process the drop event and returns true so that the drag source's
20407          * repair action does not run.
20408          * 
20409          * IMPORTANT : it should set this.success
20410          * 
20411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20412          * @param {Event} e The event
20413          * @param {Object} data An object containing arbitrary data supplied by the drag source
20414         */
20415          "drop" : true
20416     });
20417             
20418      
20419     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20420         this.el.dom, 
20421         this.ddGroup || this.group,
20422         {
20423             isTarget: true,
20424             listeners : listeners || {} 
20425            
20426         
20427         }
20428     );
20429
20430 };
20431
20432 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20433     /**
20434      * @cfg {String} overClass
20435      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20436      */
20437      /**
20438      * @cfg {String} ddGroup
20439      * The drag drop group to handle drop events for
20440      */
20441      
20442     /**
20443      * @cfg {String} dropAllowed
20444      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20445      */
20446     dropAllowed : "x-dd-drop-ok",
20447     /**
20448      * @cfg {String} dropNotAllowed
20449      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20450      */
20451     dropNotAllowed : "x-dd-drop-nodrop",
20452     /**
20453      * @cfg {boolean} success
20454      * set this after drop listener.. 
20455      */
20456     success : false,
20457     /**
20458      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20459      * if the drop point is valid for over/enter..
20460      */
20461     valid : false,
20462     // private
20463     isTarget : true,
20464
20465     // private
20466     isNotifyTarget : true,
20467     
20468     /**
20469      * @hide
20470      */
20471     notifyEnter : function(dd, e, data)
20472     {
20473         this.valid = true;
20474         this.fireEvent('enter', dd, e, data);
20475         if(this.overClass){
20476             this.el.addClass(this.overClass);
20477         }
20478         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20479             this.valid ? this.dropAllowed : this.dropNotAllowed
20480         );
20481     },
20482
20483     /**
20484      * @hide
20485      */
20486     notifyOver : function(dd, e, data)
20487     {
20488         this.valid = true;
20489         this.fireEvent('over', dd, e, data);
20490         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20491             this.valid ? this.dropAllowed : this.dropNotAllowed
20492         );
20493     },
20494
20495     /**
20496      * @hide
20497      */
20498     notifyOut : function(dd, e, data)
20499     {
20500         this.fireEvent('out', dd, e, data);
20501         if(this.overClass){
20502             this.el.removeClass(this.overClass);
20503         }
20504     },
20505
20506     /**
20507      * @hide
20508      */
20509     notifyDrop : function(dd, e, data)
20510     {
20511         this.success = false;
20512         this.fireEvent('drop', dd, e, data);
20513         return this.success;
20514     }
20515 });/*
20516  * Based on:
20517  * Ext JS Library 1.1.1
20518  * Copyright(c) 2006-2007, Ext JS, LLC.
20519  *
20520  * Originally Released Under LGPL - original licence link has changed is not relivant.
20521  *
20522  * Fork - LGPL
20523  * <script type="text/javascript">
20524  */
20525
20526
20527 /**
20528  * @class Roo.dd.DragZone
20529  * @extends Roo.dd.DragSource
20530  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20531  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20532  * @constructor
20533  * @param {String/HTMLElement/Element} el The container element
20534  * @param {Object} config
20535  */
20536 Roo.dd.DragZone = function(el, config){
20537     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20538     if(this.containerScroll){
20539         Roo.dd.ScrollManager.register(this.el);
20540     }
20541 };
20542
20543 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20544     /**
20545      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20546      * for auto scrolling during drag operations.
20547      */
20548     /**
20549      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20550      * method after a failed drop (defaults to "c3daf9" - light blue)
20551      */
20552
20553     /**
20554      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20555      * for a valid target to drag based on the mouse down. Override this method
20556      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20557      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20558      * @param {EventObject} e The mouse down event
20559      * @return {Object} The dragData
20560      */
20561     getDragData : function(e){
20562         return Roo.dd.Registry.getHandleFromEvent(e);
20563     },
20564     
20565     /**
20566      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20567      * this.dragData.ddel
20568      * @param {Number} x The x position of the click on the dragged object
20569      * @param {Number} y The y position of the click on the dragged object
20570      * @return {Boolean} true to continue the drag, false to cancel
20571      */
20572     onInitDrag : function(x, y){
20573         this.proxy.update(this.dragData.ddel.cloneNode(true));
20574         this.onStartDrag(x, y);
20575         return true;
20576     },
20577     
20578     /**
20579      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20580      */
20581     afterRepair : function(){
20582         if(Roo.enableFx){
20583             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20584         }
20585         this.dragging = false;
20586     },
20587
20588     /**
20589      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20590      * the XY of this.dragData.ddel
20591      * @param {EventObject} e The mouse up event
20592      * @return {Array} The xy location (e.g. [100, 200])
20593      */
20594     getRepairXY : function(e){
20595         return Roo.Element.fly(this.dragData.ddel).getXY();  
20596     }
20597 });/*
20598  * Based on:
20599  * Ext JS Library 1.1.1
20600  * Copyright(c) 2006-2007, Ext JS, LLC.
20601  *
20602  * Originally Released Under LGPL - original licence link has changed is not relivant.
20603  *
20604  * Fork - LGPL
20605  * <script type="text/javascript">
20606  */
20607 /**
20608  * @class Roo.dd.DropZone
20609  * @extends Roo.dd.DropTarget
20610  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20611  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20612  * @constructor
20613  * @param {String/HTMLElement/Element} el The container element
20614  * @param {Object} config
20615  */
20616 Roo.dd.DropZone = function(el, config){
20617     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20618 };
20619
20620 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20621     /**
20622      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20623      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20624      * provide your own custom lookup.
20625      * @param {Event} e The event
20626      * @return {Object} data The custom data
20627      */
20628     getTargetFromEvent : function(e){
20629         return Roo.dd.Registry.getTargetFromEvent(e);
20630     },
20631
20632     /**
20633      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20634      * that it has registered.  This method has no default implementation and should be overridden to provide
20635      * node-specific processing if necessary.
20636      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20637      * {@link #getTargetFromEvent} for this node)
20638      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20639      * @param {Event} e The event
20640      * @param {Object} data An object containing arbitrary data supplied by the drag source
20641      */
20642     onNodeEnter : function(n, dd, e, data){
20643         
20644     },
20645
20646     /**
20647      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20648      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20649      * overridden to provide the proper feedback.
20650      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20651      * {@link #getTargetFromEvent} for this node)
20652      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20653      * @param {Event} e The event
20654      * @param {Object} data An object containing arbitrary data supplied by the drag source
20655      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20656      * underlying {@link Roo.dd.StatusProxy} can be updated
20657      */
20658     onNodeOver : function(n, dd, e, data){
20659         return this.dropAllowed;
20660     },
20661
20662     /**
20663      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20664      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20665      * node-specific processing if necessary.
20666      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20667      * {@link #getTargetFromEvent} for this node)
20668      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20669      * @param {Event} e The event
20670      * @param {Object} data An object containing arbitrary data supplied by the drag source
20671      */
20672     onNodeOut : function(n, dd, e, data){
20673         
20674     },
20675
20676     /**
20677      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20678      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20679      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20680      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20681      * {@link #getTargetFromEvent} for this node)
20682      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20683      * @param {Event} e The event
20684      * @param {Object} data An object containing arbitrary data supplied by the drag source
20685      * @return {Boolean} True if the drop was valid, else false
20686      */
20687     onNodeDrop : function(n, dd, e, data){
20688         return false;
20689     },
20690
20691     /**
20692      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20693      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20694      * it should be overridden to provide the proper feedback if necessary.
20695      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20696      * @param {Event} e The event
20697      * @param {Object} data An object containing arbitrary data supplied by the drag source
20698      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20699      * underlying {@link Roo.dd.StatusProxy} can be updated
20700      */
20701     onContainerOver : function(dd, e, data){
20702         return this.dropNotAllowed;
20703     },
20704
20705     /**
20706      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20707      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20708      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20709      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20710      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20711      * @param {Event} e The event
20712      * @param {Object} data An object containing arbitrary data supplied by the drag source
20713      * @return {Boolean} True if the drop was valid, else false
20714      */
20715     onContainerDrop : function(dd, e, data){
20716         return false;
20717     },
20718
20719     /**
20720      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20721      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20722      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20723      * you should override this method and provide a custom implementation.
20724      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20725      * @param {Event} e The event
20726      * @param {Object} data An object containing arbitrary data supplied by the drag source
20727      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20728      * underlying {@link Roo.dd.StatusProxy} can be updated
20729      */
20730     notifyEnter : function(dd, e, data){
20731         return this.dropNotAllowed;
20732     },
20733
20734     /**
20735      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20736      * This method will be called on every mouse movement while the drag source is over the drop zone.
20737      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20738      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20739      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20740      * registered node, it will call {@link #onContainerOver}.
20741      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20742      * @param {Event} e The event
20743      * @param {Object} data An object containing arbitrary data supplied by the drag source
20744      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20745      * underlying {@link Roo.dd.StatusProxy} can be updated
20746      */
20747     notifyOver : function(dd, e, data){
20748         var n = this.getTargetFromEvent(e);
20749         if(!n){ // not over valid drop target
20750             if(this.lastOverNode){
20751                 this.onNodeOut(this.lastOverNode, dd, e, data);
20752                 this.lastOverNode = null;
20753             }
20754             return this.onContainerOver(dd, e, data);
20755         }
20756         if(this.lastOverNode != n){
20757             if(this.lastOverNode){
20758                 this.onNodeOut(this.lastOverNode, dd, e, data);
20759             }
20760             this.onNodeEnter(n, dd, e, data);
20761             this.lastOverNode = n;
20762         }
20763         return this.onNodeOver(n, dd, e, data);
20764     },
20765
20766     /**
20767      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20768      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20769      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20770      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20771      * @param {Event} e The event
20772      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20773      */
20774     notifyOut : function(dd, e, data){
20775         if(this.lastOverNode){
20776             this.onNodeOut(this.lastOverNode, dd, e, data);
20777             this.lastOverNode = null;
20778         }
20779     },
20780
20781     /**
20782      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20783      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20784      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20785      * otherwise it will call {@link #onContainerDrop}.
20786      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20787      * @param {Event} e The event
20788      * @param {Object} data An object containing arbitrary data supplied by the drag source
20789      * @return {Boolean} True if the drop was valid, else false
20790      */
20791     notifyDrop : function(dd, e, data){
20792         if(this.lastOverNode){
20793             this.onNodeOut(this.lastOverNode, dd, e, data);
20794             this.lastOverNode = null;
20795         }
20796         var n = this.getTargetFromEvent(e);
20797         return n ?
20798             this.onNodeDrop(n, dd, e, data) :
20799             this.onContainerDrop(dd, e, data);
20800     },
20801
20802     // private
20803     triggerCacheRefresh : function(){
20804         Roo.dd.DDM.refreshCache(this.groups);
20805     }  
20806 });/*
20807  * Based on:
20808  * Ext JS Library 1.1.1
20809  * Copyright(c) 2006-2007, Ext JS, LLC.
20810  *
20811  * Originally Released Under LGPL - original licence link has changed is not relivant.
20812  *
20813  * Fork - LGPL
20814  * <script type="text/javascript">
20815  */
20816
20817
20818 /**
20819  * @class Roo.data.SortTypes
20820  * @singleton
20821  * Defines the default sorting (casting?) comparison functions used when sorting data.
20822  */
20823 Roo.data.SortTypes = {
20824     /**
20825      * Default sort that does nothing
20826      * @param {Mixed} s The value being converted
20827      * @return {Mixed} The comparison value
20828      */
20829     none : function(s){
20830         return s;
20831     },
20832     
20833     /**
20834      * The regular expression used to strip tags
20835      * @type {RegExp}
20836      * @property
20837      */
20838     stripTagsRE : /<\/?[^>]+>/gi,
20839     
20840     /**
20841      * Strips all HTML tags to sort on text only
20842      * @param {Mixed} s The value being converted
20843      * @return {String} The comparison value
20844      */
20845     asText : function(s){
20846         return String(s).replace(this.stripTagsRE, "");
20847     },
20848     
20849     /**
20850      * Strips all HTML tags to sort on text only - Case insensitive
20851      * @param {Mixed} s The value being converted
20852      * @return {String} The comparison value
20853      */
20854     asUCText : function(s){
20855         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20856     },
20857     
20858     /**
20859      * Case insensitive string
20860      * @param {Mixed} s The value being converted
20861      * @return {String} The comparison value
20862      */
20863     asUCString : function(s) {
20864         return String(s).toUpperCase();
20865     },
20866     
20867     /**
20868      * Date sorting
20869      * @param {Mixed} s The value being converted
20870      * @return {Number} The comparison value
20871      */
20872     asDate : function(s) {
20873         if(!s){
20874             return 0;
20875         }
20876         if(s instanceof Date){
20877             return s.getTime();
20878         }
20879         return Date.parse(String(s));
20880     },
20881     
20882     /**
20883      * Float sorting
20884      * @param {Mixed} s The value being converted
20885      * @return {Float} The comparison value
20886      */
20887     asFloat : function(s) {
20888         var val = parseFloat(String(s).replace(/,/g, ""));
20889         if(isNaN(val)) val = 0;
20890         return val;
20891     },
20892     
20893     /**
20894      * Integer sorting
20895      * @param {Mixed} s The value being converted
20896      * @return {Number} The comparison value
20897      */
20898     asInt : function(s) {
20899         var val = parseInt(String(s).replace(/,/g, ""));
20900         if(isNaN(val)) val = 0;
20901         return val;
20902     }
20903 };/*
20904  * Based on:
20905  * Ext JS Library 1.1.1
20906  * Copyright(c) 2006-2007, Ext JS, LLC.
20907  *
20908  * Originally Released Under LGPL - original licence link has changed is not relivant.
20909  *
20910  * Fork - LGPL
20911  * <script type="text/javascript">
20912  */
20913
20914 /**
20915 * @class Roo.data.Record
20916  * Instances of this class encapsulate both record <em>definition</em> information, and record
20917  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20918  * to access Records cached in an {@link Roo.data.Store} object.<br>
20919  * <p>
20920  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20921  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20922  * objects.<br>
20923  * <p>
20924  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20925  * @constructor
20926  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20927  * {@link #create}. The parameters are the same.
20928  * @param {Array} data An associative Array of data values keyed by the field name.
20929  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20930  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20931  * not specified an integer id is generated.
20932  */
20933 Roo.data.Record = function(data, id){
20934     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20935     this.data = data;
20936 };
20937
20938 /**
20939  * Generate a constructor for a specific record layout.
20940  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20941  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20942  * Each field definition object may contain the following properties: <ul>
20943  * <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,
20944  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20945  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20946  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20947  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20948  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20949  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20950  * this may be omitted.</p></li>
20951  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20952  * <ul><li>auto (Default, implies no conversion)</li>
20953  * <li>string</li>
20954  * <li>int</li>
20955  * <li>float</li>
20956  * <li>boolean</li>
20957  * <li>date</li></ul></p></li>
20958  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20959  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20960  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20961  * by the Reader into an object that will be stored in the Record. It is passed the
20962  * following parameters:<ul>
20963  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20964  * </ul></p></li>
20965  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20966  * </ul>
20967  * <br>usage:<br><pre><code>
20968 var TopicRecord = Roo.data.Record.create(
20969     {name: 'title', mapping: 'topic_title'},
20970     {name: 'author', mapping: 'username'},
20971     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20972     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20973     {name: 'lastPoster', mapping: 'user2'},
20974     {name: 'excerpt', mapping: 'post_text'}
20975 );
20976
20977 var myNewRecord = new TopicRecord({
20978     title: 'Do my job please',
20979     author: 'noobie',
20980     totalPosts: 1,
20981     lastPost: new Date(),
20982     lastPoster: 'Animal',
20983     excerpt: 'No way dude!'
20984 });
20985 myStore.add(myNewRecord);
20986 </code></pre>
20987  * @method create
20988  * @static
20989  */
20990 Roo.data.Record.create = function(o){
20991     var f = function(){
20992         f.superclass.constructor.apply(this, arguments);
20993     };
20994     Roo.extend(f, Roo.data.Record);
20995     var p = f.prototype;
20996     p.fields = new Roo.util.MixedCollection(false, function(field){
20997         return field.name;
20998     });
20999     for(var i = 0, len = o.length; i < len; i++){
21000         p.fields.add(new Roo.data.Field(o[i]));
21001     }
21002     f.getField = function(name){
21003         return p.fields.get(name);  
21004     };
21005     return f;
21006 };
21007
21008 Roo.data.Record.AUTO_ID = 1000;
21009 Roo.data.Record.EDIT = 'edit';
21010 Roo.data.Record.REJECT = 'reject';
21011 Roo.data.Record.COMMIT = 'commit';
21012
21013 Roo.data.Record.prototype = {
21014     /**
21015      * Readonly flag - true if this record has been modified.
21016      * @type Boolean
21017      */
21018     dirty : false,
21019     editing : false,
21020     error: null,
21021     modified: null,
21022
21023     // private
21024     join : function(store){
21025         this.store = store;
21026     },
21027
21028     /**
21029      * Set the named field to the specified value.
21030      * @param {String} name The name of the field to set.
21031      * @param {Object} value The value to set the field to.
21032      */
21033     set : function(name, value){
21034         if(this.data[name] == value){
21035             return;
21036         }
21037         this.dirty = true;
21038         if(!this.modified){
21039             this.modified = {};
21040         }
21041         if(typeof this.modified[name] == 'undefined'){
21042             this.modified[name] = this.data[name];
21043         }
21044         this.data[name] = value;
21045         if(!this.editing && this.store){
21046             this.store.afterEdit(this);
21047         }       
21048     },
21049
21050     /**
21051      * Get the value of the named field.
21052      * @param {String} name The name of the field to get the value of.
21053      * @return {Object} The value of the field.
21054      */
21055     get : function(name){
21056         return this.data[name]; 
21057     },
21058
21059     // private
21060     beginEdit : function(){
21061         this.editing = true;
21062         this.modified = {}; 
21063     },
21064
21065     // private
21066     cancelEdit : function(){
21067         this.editing = false;
21068         delete this.modified;
21069     },
21070
21071     // private
21072     endEdit : function(){
21073         this.editing = false;
21074         if(this.dirty && this.store){
21075             this.store.afterEdit(this);
21076         }
21077     },
21078
21079     /**
21080      * Usually called by the {@link Roo.data.Store} which owns the Record.
21081      * Rejects all changes made to the Record since either creation, or the last commit operation.
21082      * Modified fields are reverted to their original values.
21083      * <p>
21084      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21085      * of reject operations.
21086      */
21087     reject : function(){
21088         var m = this.modified;
21089         for(var n in m){
21090             if(typeof m[n] != "function"){
21091                 this.data[n] = m[n];
21092             }
21093         }
21094         this.dirty = false;
21095         delete this.modified;
21096         this.editing = false;
21097         if(this.store){
21098             this.store.afterReject(this);
21099         }
21100     },
21101
21102     /**
21103      * Usually called by the {@link Roo.data.Store} which owns the Record.
21104      * Commits all changes made to the Record since either creation, or the last commit operation.
21105      * <p>
21106      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21107      * of commit operations.
21108      */
21109     commit : function(){
21110         this.dirty = false;
21111         delete this.modified;
21112         this.editing = false;
21113         if(this.store){
21114             this.store.afterCommit(this);
21115         }
21116     },
21117
21118     // private
21119     hasError : function(){
21120         return this.error != null;
21121     },
21122
21123     // private
21124     clearError : function(){
21125         this.error = null;
21126     },
21127
21128     /**
21129      * Creates a copy of this record.
21130      * @param {String} id (optional) A new record id if you don't want to use this record's id
21131      * @return {Record}
21132      */
21133     copy : function(newId) {
21134         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21135     }
21136 };/*
21137  * Based on:
21138  * Ext JS Library 1.1.1
21139  * Copyright(c) 2006-2007, Ext JS, LLC.
21140  *
21141  * Originally Released Under LGPL - original licence link has changed is not relivant.
21142  *
21143  * Fork - LGPL
21144  * <script type="text/javascript">
21145  */
21146
21147
21148
21149 /**
21150  * @class Roo.data.Store
21151  * @extends Roo.util.Observable
21152  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21153  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21154  * <p>
21155  * 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
21156  * has no knowledge of the format of the data returned by the Proxy.<br>
21157  * <p>
21158  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21159  * instances from the data object. These records are cached and made available through accessor functions.
21160  * @constructor
21161  * Creates a new Store.
21162  * @param {Object} config A config object containing the objects needed for the Store to access data,
21163  * and read the data into Records.
21164  */
21165 Roo.data.Store = function(config){
21166     this.data = new Roo.util.MixedCollection(false);
21167     this.data.getKey = function(o){
21168         return o.id;
21169     };
21170     this.baseParams = {};
21171     // private
21172     this.paramNames = {
21173         "start" : "start",
21174         "limit" : "limit",
21175         "sort" : "sort",
21176         "dir" : "dir",
21177         "multisort" : "_multisort"
21178     };
21179
21180     if(config && config.data){
21181         this.inlineData = config.data;
21182         delete config.data;
21183     }
21184
21185     Roo.apply(this, config);
21186     
21187     if(this.reader){ // reader passed
21188         this.reader = Roo.factory(this.reader, Roo.data);
21189         this.reader.xmodule = this.xmodule || false;
21190         if(!this.recordType){
21191             this.recordType = this.reader.recordType;
21192         }
21193         if(this.reader.onMetaChange){
21194             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21195         }
21196     }
21197
21198     if(this.recordType){
21199         this.fields = this.recordType.prototype.fields;
21200     }
21201     this.modified = [];
21202
21203     this.addEvents({
21204         /**
21205          * @event datachanged
21206          * Fires when the data cache has changed, and a widget which is using this Store
21207          * as a Record cache should refresh its view.
21208          * @param {Store} this
21209          */
21210         datachanged : true,
21211         /**
21212          * @event metachange
21213          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21214          * @param {Store} this
21215          * @param {Object} meta The JSON metadata
21216          */
21217         metachange : true,
21218         /**
21219          * @event add
21220          * Fires when Records have been added to the Store
21221          * @param {Store} this
21222          * @param {Roo.data.Record[]} records The array of Records added
21223          * @param {Number} index The index at which the record(s) were added
21224          */
21225         add : true,
21226         /**
21227          * @event remove
21228          * Fires when a Record has been removed from the Store
21229          * @param {Store} this
21230          * @param {Roo.data.Record} record The Record that was removed
21231          * @param {Number} index The index at which the record was removed
21232          */
21233         remove : true,
21234         /**
21235          * @event update
21236          * Fires when a Record has been updated
21237          * @param {Store} this
21238          * @param {Roo.data.Record} record The Record that was updated
21239          * @param {String} operation The update operation being performed.  Value may be one of:
21240          * <pre><code>
21241  Roo.data.Record.EDIT
21242  Roo.data.Record.REJECT
21243  Roo.data.Record.COMMIT
21244          * </code></pre>
21245          */
21246         update : true,
21247         /**
21248          * @event clear
21249          * Fires when the data cache has been cleared.
21250          * @param {Store} this
21251          */
21252         clear : true,
21253         /**
21254          * @event beforeload
21255          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21256          * the load action will be canceled.
21257          * @param {Store} this
21258          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21259          */
21260         beforeload : true,
21261         /**
21262          * @event beforeloadadd
21263          * Fires after a new set of Records has been loaded.
21264          * @param {Store} this
21265          * @param {Roo.data.Record[]} records The Records that were loaded
21266          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21267          */
21268         beforeloadadd : true,
21269         /**
21270          * @event load
21271          * Fires after a new set of Records has been loaded, before they are added to the store.
21272          * @param {Store} this
21273          * @param {Roo.data.Record[]} records The Records that were loaded
21274          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21275          * @params {Object} return from reader
21276          */
21277         load : true,
21278         /**
21279          * @event loadexception
21280          * Fires if an exception occurs in the Proxy during loading.
21281          * Called with the signature of the Proxy's "loadexception" event.
21282          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21283          * 
21284          * @param {Proxy} 
21285          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21286          * @param {Object} load options 
21287          * @param {Object} jsonData from your request (normally this contains the Exception)
21288          */
21289         loadexception : true
21290     });
21291     
21292     if(this.proxy){
21293         this.proxy = Roo.factory(this.proxy, Roo.data);
21294         this.proxy.xmodule = this.xmodule || false;
21295         this.relayEvents(this.proxy,  ["loadexception"]);
21296     }
21297     this.sortToggle = {};
21298     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21299
21300     Roo.data.Store.superclass.constructor.call(this);
21301
21302     if(this.inlineData){
21303         this.loadData(this.inlineData);
21304         delete this.inlineData;
21305     }
21306 };
21307
21308 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21309      /**
21310     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21311     * without a remote query - used by combo/forms at present.
21312     */
21313     
21314     /**
21315     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21316     */
21317     /**
21318     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21319     */
21320     /**
21321     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21322     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21323     */
21324     /**
21325     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21326     * on any HTTP request
21327     */
21328     /**
21329     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21330     */
21331     /**
21332     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21333     */
21334     multiSort: false,
21335     /**
21336     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21337     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21338     */
21339     remoteSort : false,
21340
21341     /**
21342     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21343      * loaded or when a record is removed. (defaults to false).
21344     */
21345     pruneModifiedRecords : false,
21346
21347     // private
21348     lastOptions : null,
21349
21350     /**
21351      * Add Records to the Store and fires the add event.
21352      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21353      */
21354     add : function(records){
21355         records = [].concat(records);
21356         for(var i = 0, len = records.length; i < len; i++){
21357             records[i].join(this);
21358         }
21359         var index = this.data.length;
21360         this.data.addAll(records);
21361         this.fireEvent("add", this, records, index);
21362     },
21363
21364     /**
21365      * Remove a Record from the Store and fires the remove event.
21366      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21367      */
21368     remove : function(record){
21369         var index = this.data.indexOf(record);
21370         this.data.removeAt(index);
21371         if(this.pruneModifiedRecords){
21372             this.modified.remove(record);
21373         }
21374         this.fireEvent("remove", this, record, index);
21375     },
21376
21377     /**
21378      * Remove all Records from the Store and fires the clear event.
21379      */
21380     removeAll : function(){
21381         this.data.clear();
21382         if(this.pruneModifiedRecords){
21383             this.modified = [];
21384         }
21385         this.fireEvent("clear", this);
21386     },
21387
21388     /**
21389      * Inserts Records to the Store at the given index and fires the add event.
21390      * @param {Number} index The start index at which to insert the passed Records.
21391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21392      */
21393     insert : function(index, records){
21394         records = [].concat(records);
21395         for(var i = 0, len = records.length; i < len; i++){
21396             this.data.insert(index, records[i]);
21397             records[i].join(this);
21398         }
21399         this.fireEvent("add", this, records, index);
21400     },
21401
21402     /**
21403      * Get the index within the cache of the passed Record.
21404      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21405      * @return {Number} The index of the passed Record. Returns -1 if not found.
21406      */
21407     indexOf : function(record){
21408         return this.data.indexOf(record);
21409     },
21410
21411     /**
21412      * Get the index within the cache of the Record with the passed id.
21413      * @param {String} id The id of the Record to find.
21414      * @return {Number} The index of the Record. Returns -1 if not found.
21415      */
21416     indexOfId : function(id){
21417         return this.data.indexOfKey(id);
21418     },
21419
21420     /**
21421      * Get the Record with the specified id.
21422      * @param {String} id The id of the Record to find.
21423      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21424      */
21425     getById : function(id){
21426         return this.data.key(id);
21427     },
21428
21429     /**
21430      * Get the Record at the specified index.
21431      * @param {Number} index The index of the Record to find.
21432      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21433      */
21434     getAt : function(index){
21435         return this.data.itemAt(index);
21436     },
21437
21438     /**
21439      * Returns a range of Records between specified indices.
21440      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21441      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21442      * @return {Roo.data.Record[]} An array of Records
21443      */
21444     getRange : function(start, end){
21445         return this.data.getRange(start, end);
21446     },
21447
21448     // private
21449     storeOptions : function(o){
21450         o = Roo.apply({}, o);
21451         delete o.callback;
21452         delete o.scope;
21453         this.lastOptions = o;
21454     },
21455
21456     /**
21457      * Loads the Record cache from the configured Proxy using the configured Reader.
21458      * <p>
21459      * If using remote paging, then the first load call must specify the <em>start</em>
21460      * and <em>limit</em> properties in the options.params property to establish the initial
21461      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21462      * <p>
21463      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21464      * and this call will return before the new data has been loaded. Perform any post-processing
21465      * in a callback function, or in a "load" event handler.</strong>
21466      * <p>
21467      * @param {Object} options An object containing properties which control loading options:<ul>
21468      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21469      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21470      * passed the following arguments:<ul>
21471      * <li>r : Roo.data.Record[]</li>
21472      * <li>options: Options object from the load call</li>
21473      * <li>success: Boolean success indicator</li></ul></li>
21474      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21475      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21476      * </ul>
21477      */
21478     load : function(options){
21479         options = options || {};
21480         if(this.fireEvent("beforeload", this, options) !== false){
21481             this.storeOptions(options);
21482             var p = Roo.apply(options.params || {}, this.baseParams);
21483             // if meta was not loaded from remote source.. try requesting it.
21484             if (!this.reader.metaFromRemote) {
21485                 p._requestMeta = 1;
21486             }
21487             if(this.sortInfo && this.remoteSort){
21488                 var pn = this.paramNames;
21489                 p[pn["sort"]] = this.sortInfo.field;
21490                 p[pn["dir"]] = this.sortInfo.direction;
21491             }
21492             if (this.multiSort) {
21493                 var pn = this.paramNames;
21494                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21495             }
21496             
21497             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21498         }
21499     },
21500
21501     /**
21502      * Reloads the Record cache from the configured Proxy using the configured Reader and
21503      * the options from the last load operation performed.
21504      * @param {Object} options (optional) An object containing properties which may override the options
21505      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21506      * the most recently used options are reused).
21507      */
21508     reload : function(options){
21509         this.load(Roo.applyIf(options||{}, this.lastOptions));
21510     },
21511
21512     // private
21513     // Called as a callback by the Reader during a load operation.
21514     loadRecords : function(o, options, success){
21515         if(!o || success === false){
21516             if(success !== false){
21517                 this.fireEvent("load", this, [], options, o);
21518             }
21519             if(options.callback){
21520                 options.callback.call(options.scope || this, [], options, false);
21521             }
21522             return;
21523         }
21524         // if data returned failure - throw an exception.
21525         if (o.success === false) {
21526             // show a message if no listener is registered.
21527             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21528                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21529             }
21530             // loadmask wil be hooked into this..
21531             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21532             return;
21533         }
21534         var r = o.records, t = o.totalRecords || r.length;
21535         
21536         this.fireEvent("beforeloadadd", this, r, options, o);
21537         
21538         if(!options || options.add !== true){
21539             if(this.pruneModifiedRecords){
21540                 this.modified = [];
21541             }
21542             for(var i = 0, len = r.length; i < len; i++){
21543                 r[i].join(this);
21544             }
21545             if(this.snapshot){
21546                 this.data = this.snapshot;
21547                 delete this.snapshot;
21548             }
21549             this.data.clear();
21550             this.data.addAll(r);
21551             this.totalLength = t;
21552             this.applySort();
21553             this.fireEvent("datachanged", this);
21554         }else{
21555             this.totalLength = Math.max(t, this.data.length+r.length);
21556             this.add(r);
21557         }
21558         this.fireEvent("load", this, r, options, o);
21559         if(options.callback){
21560             options.callback.call(options.scope || this, r, options, true);
21561         }
21562     },
21563
21564
21565     /**
21566      * Loads data from a passed data block. A Reader which understands the format of the data
21567      * must have been configured in the constructor.
21568      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21569      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21570      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21571      */
21572     loadData : function(o, append){
21573         var r = this.reader.readRecords(o);
21574         this.loadRecords(r, {add: append}, true);
21575     },
21576
21577     /**
21578      * Gets the number of cached records.
21579      * <p>
21580      * <em>If using paging, this may not be the total size of the dataset. If the data object
21581      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21582      * the data set size</em>
21583      */
21584     getCount : function(){
21585         return this.data.length || 0;
21586     },
21587
21588     /**
21589      * Gets the total number of records in the dataset as returned by the server.
21590      * <p>
21591      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21592      * the dataset size</em>
21593      */
21594     getTotalCount : function(){
21595         return this.totalLength || 0;
21596     },
21597
21598     /**
21599      * Returns the sort state of the Store as an object with two properties:
21600      * <pre><code>
21601  field {String} The name of the field by which the Records are sorted
21602  direction {String} The sort order, "ASC" or "DESC"
21603      * </code></pre>
21604      */
21605     getSortState : function(){
21606         return this.sortInfo;
21607     },
21608
21609     // private
21610     applySort : function(){
21611         if(this.sortInfo && !this.remoteSort){
21612             var s = this.sortInfo, f = s.field;
21613             var st = this.fields.get(f).sortType;
21614             var fn = function(r1, r2){
21615                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21616                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21617             };
21618             this.data.sort(s.direction, fn);
21619             if(this.snapshot && this.snapshot != this.data){
21620                 this.snapshot.sort(s.direction, fn);
21621             }
21622         }
21623     },
21624
21625     /**
21626      * Sets the default sort column and order to be used by the next load operation.
21627      * @param {String} fieldName The name of the field to sort by.
21628      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21629      */
21630     setDefaultSort : function(field, dir){
21631         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21632     },
21633
21634     /**
21635      * Sort the Records.
21636      * If remote sorting is used, the sort is performed on the server, and the cache is
21637      * reloaded. If local sorting is used, the cache is sorted internally.
21638      * @param {String} fieldName The name of the field to sort by.
21639      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21640      */
21641     sort : function(fieldName, dir){
21642         var f = this.fields.get(fieldName);
21643         if(!dir){
21644             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21645             
21646             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21647                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21648             }else{
21649                 dir = f.sortDir;
21650             }
21651         }
21652         this.sortToggle[f.name] = dir;
21653         this.sortInfo = {field: f.name, direction: dir};
21654         if(!this.remoteSort){
21655             this.applySort();
21656             this.fireEvent("datachanged", this);
21657         }else{
21658             this.load(this.lastOptions);
21659         }
21660     },
21661
21662     /**
21663      * Calls the specified function for each of the Records in the cache.
21664      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21665      * Returning <em>false</em> aborts and exits the iteration.
21666      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21667      */
21668     each : function(fn, scope){
21669         this.data.each(fn, scope);
21670     },
21671
21672     /**
21673      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21674      * (e.g., during paging).
21675      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21676      */
21677     getModifiedRecords : function(){
21678         return this.modified;
21679     },
21680
21681     // private
21682     createFilterFn : function(property, value, anyMatch){
21683         if(!value.exec){ // not a regex
21684             value = String(value);
21685             if(value.length == 0){
21686                 return false;
21687             }
21688             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21689         }
21690         return function(r){
21691             return value.test(r.data[property]);
21692         };
21693     },
21694
21695     /**
21696      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21697      * @param {String} property A field on your records
21698      * @param {Number} start The record index to start at (defaults to 0)
21699      * @param {Number} end The last record index to include (defaults to length - 1)
21700      * @return {Number} The sum
21701      */
21702     sum : function(property, start, end){
21703         var rs = this.data.items, v = 0;
21704         start = start || 0;
21705         end = (end || end === 0) ? end : rs.length-1;
21706
21707         for(var i = start; i <= end; i++){
21708             v += (rs[i].data[property] || 0);
21709         }
21710         return v;
21711     },
21712
21713     /**
21714      * Filter the records by a specified property.
21715      * @param {String} field A field on your records
21716      * @param {String/RegExp} value Either a string that the field
21717      * should start with or a RegExp to test against the field
21718      * @param {Boolean} anyMatch True to match any part not just the beginning
21719      */
21720     filter : function(property, value, anyMatch){
21721         var fn = this.createFilterFn(property, value, anyMatch);
21722         return fn ? this.filterBy(fn) : this.clearFilter();
21723     },
21724
21725     /**
21726      * Filter by a function. The specified function will be called with each
21727      * record in this data source. If the function returns true the record is included,
21728      * otherwise it is filtered.
21729      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21730      * @param {Object} scope (optional) The scope of the function (defaults to this)
21731      */
21732     filterBy : function(fn, scope){
21733         this.snapshot = this.snapshot || this.data;
21734         this.data = this.queryBy(fn, scope||this);
21735         this.fireEvent("datachanged", this);
21736     },
21737
21738     /**
21739      * Query the records by a specified property.
21740      * @param {String} field A field on your records
21741      * @param {String/RegExp} value Either a string that the field
21742      * should start with or a RegExp to test against the field
21743      * @param {Boolean} anyMatch True to match any part not just the beginning
21744      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21745      */
21746     query : function(property, value, anyMatch){
21747         var fn = this.createFilterFn(property, value, anyMatch);
21748         return fn ? this.queryBy(fn) : this.data.clone();
21749     },
21750
21751     /**
21752      * Query by a function. The specified function will be called with each
21753      * record in this data source. If the function returns true the record is included
21754      * in the results.
21755      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21756      * @param {Object} scope (optional) The scope of the function (defaults to this)
21757       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21758      **/
21759     queryBy : function(fn, scope){
21760         var data = this.snapshot || this.data;
21761         return data.filterBy(fn, scope||this);
21762     },
21763
21764     /**
21765      * Collects unique values for a particular dataIndex from this store.
21766      * @param {String} dataIndex The property to collect
21767      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21768      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21769      * @return {Array} An array of the unique values
21770      **/
21771     collect : function(dataIndex, allowNull, bypassFilter){
21772         var d = (bypassFilter === true && this.snapshot) ?
21773                 this.snapshot.items : this.data.items;
21774         var v, sv, r = [], l = {};
21775         for(var i = 0, len = d.length; i < len; i++){
21776             v = d[i].data[dataIndex];
21777             sv = String(v);
21778             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21779                 l[sv] = true;
21780                 r[r.length] = v;
21781             }
21782         }
21783         return r;
21784     },
21785
21786     /**
21787      * Revert to a view of the Record cache with no filtering applied.
21788      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21789      */
21790     clearFilter : function(suppressEvent){
21791         if(this.snapshot && this.snapshot != this.data){
21792             this.data = this.snapshot;
21793             delete this.snapshot;
21794             if(suppressEvent !== true){
21795                 this.fireEvent("datachanged", this);
21796             }
21797         }
21798     },
21799
21800     // private
21801     afterEdit : function(record){
21802         if(this.modified.indexOf(record) == -1){
21803             this.modified.push(record);
21804         }
21805         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21806     },
21807     
21808     // private
21809     afterReject : function(record){
21810         this.modified.remove(record);
21811         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21812     },
21813
21814     // private
21815     afterCommit : function(record){
21816         this.modified.remove(record);
21817         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21818     },
21819
21820     /**
21821      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21822      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21823      */
21824     commitChanges : function(){
21825         var m = this.modified.slice(0);
21826         this.modified = [];
21827         for(var i = 0, len = m.length; i < len; i++){
21828             m[i].commit();
21829         }
21830     },
21831
21832     /**
21833      * Cancel outstanding changes on all changed records.
21834      */
21835     rejectChanges : function(){
21836         var m = this.modified.slice(0);
21837         this.modified = [];
21838         for(var i = 0, len = m.length; i < len; i++){
21839             m[i].reject();
21840         }
21841     },
21842
21843     onMetaChange : function(meta, rtype, o){
21844         this.recordType = rtype;
21845         this.fields = rtype.prototype.fields;
21846         delete this.snapshot;
21847         this.sortInfo = meta.sortInfo || this.sortInfo;
21848         this.modified = [];
21849         this.fireEvent('metachange', this, this.reader.meta);
21850     },
21851     
21852     moveIndex : function(data, type)
21853     {
21854         var index = this.indexOf(data);
21855         
21856         var newIndex = index + type;
21857         
21858         this.remove(data);
21859         
21860         this.insert(newIndex, data);
21861         
21862     }
21863 });/*
21864  * Based on:
21865  * Ext JS Library 1.1.1
21866  * Copyright(c) 2006-2007, Ext JS, LLC.
21867  *
21868  * Originally Released Under LGPL - original licence link has changed is not relivant.
21869  *
21870  * Fork - LGPL
21871  * <script type="text/javascript">
21872  */
21873
21874 /**
21875  * @class Roo.data.SimpleStore
21876  * @extends Roo.data.Store
21877  * Small helper class to make creating Stores from Array data easier.
21878  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21879  * @cfg {Array} fields An array of field definition objects, or field name strings.
21880  * @cfg {Array} data The multi-dimensional array of data
21881  * @constructor
21882  * @param {Object} config
21883  */
21884 Roo.data.SimpleStore = function(config){
21885     Roo.data.SimpleStore.superclass.constructor.call(this, {
21886         isLocal : true,
21887         reader: new Roo.data.ArrayReader({
21888                 id: config.id
21889             },
21890             Roo.data.Record.create(config.fields)
21891         ),
21892         proxy : new Roo.data.MemoryProxy(config.data)
21893     });
21894     this.load();
21895 };
21896 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21897  * Based on:
21898  * Ext JS Library 1.1.1
21899  * Copyright(c) 2006-2007, Ext JS, LLC.
21900  *
21901  * Originally Released Under LGPL - original licence link has changed is not relivant.
21902  *
21903  * Fork - LGPL
21904  * <script type="text/javascript">
21905  */
21906
21907 /**
21908 /**
21909  * @extends Roo.data.Store
21910  * @class Roo.data.JsonStore
21911  * Small helper class to make creating Stores for JSON data easier. <br/>
21912 <pre><code>
21913 var store = new Roo.data.JsonStore({
21914     url: 'get-images.php',
21915     root: 'images',
21916     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21917 });
21918 </code></pre>
21919  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21920  * JsonReader and HttpProxy (unless inline data is provided).</b>
21921  * @cfg {Array} fields An array of field definition objects, or field name strings.
21922  * @constructor
21923  * @param {Object} config
21924  */
21925 Roo.data.JsonStore = function(c){
21926     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21927         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21928         reader: new Roo.data.JsonReader(c, c.fields)
21929     }));
21930 };
21931 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21932  * Based on:
21933  * Ext JS Library 1.1.1
21934  * Copyright(c) 2006-2007, Ext JS, LLC.
21935  *
21936  * Originally Released Under LGPL - original licence link has changed is not relivant.
21937  *
21938  * Fork - LGPL
21939  * <script type="text/javascript">
21940  */
21941
21942  
21943 Roo.data.Field = function(config){
21944     if(typeof config == "string"){
21945         config = {name: config};
21946     }
21947     Roo.apply(this, config);
21948     
21949     if(!this.type){
21950         this.type = "auto";
21951     }
21952     
21953     var st = Roo.data.SortTypes;
21954     // named sortTypes are supported, here we look them up
21955     if(typeof this.sortType == "string"){
21956         this.sortType = st[this.sortType];
21957     }
21958     
21959     // set default sortType for strings and dates
21960     if(!this.sortType){
21961         switch(this.type){
21962             case "string":
21963                 this.sortType = st.asUCString;
21964                 break;
21965             case "date":
21966                 this.sortType = st.asDate;
21967                 break;
21968             default:
21969                 this.sortType = st.none;
21970         }
21971     }
21972
21973     // define once
21974     var stripRe = /[\$,%]/g;
21975
21976     // prebuilt conversion function for this field, instead of
21977     // switching every time we're reading a value
21978     if(!this.convert){
21979         var cv, dateFormat = this.dateFormat;
21980         switch(this.type){
21981             case "":
21982             case "auto":
21983             case undefined:
21984                 cv = function(v){ return v; };
21985                 break;
21986             case "string":
21987                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21988                 break;
21989             case "int":
21990                 cv = function(v){
21991                     return v !== undefined && v !== null && v !== '' ?
21992                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21993                     };
21994                 break;
21995             case "float":
21996                 cv = function(v){
21997                     return v !== undefined && v !== null && v !== '' ?
21998                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21999                     };
22000                 break;
22001             case "bool":
22002             case "boolean":
22003                 cv = function(v){ return v === true || v === "true" || v == 1; };
22004                 break;
22005             case "date":
22006                 cv = function(v){
22007                     if(!v){
22008                         return '';
22009                     }
22010                     if(v instanceof Date){
22011                         return v;
22012                     }
22013                     if(dateFormat){
22014                         if(dateFormat == "timestamp"){
22015                             return new Date(v*1000);
22016                         }
22017                         return Date.parseDate(v, dateFormat);
22018                     }
22019                     var parsed = Date.parse(v);
22020                     return parsed ? new Date(parsed) : null;
22021                 };
22022              break;
22023             
22024         }
22025         this.convert = cv;
22026     }
22027 };
22028
22029 Roo.data.Field.prototype = {
22030     dateFormat: null,
22031     defaultValue: "",
22032     mapping: null,
22033     sortType : null,
22034     sortDir : "ASC"
22035 };/*
22036  * Based on:
22037  * Ext JS Library 1.1.1
22038  * Copyright(c) 2006-2007, Ext JS, LLC.
22039  *
22040  * Originally Released Under LGPL - original licence link has changed is not relivant.
22041  *
22042  * Fork - LGPL
22043  * <script type="text/javascript">
22044  */
22045  
22046 // Base class for reading structured data from a data source.  This class is intended to be
22047 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22048
22049 /**
22050  * @class Roo.data.DataReader
22051  * Base class for reading structured data from a data source.  This class is intended to be
22052  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22053  */
22054
22055 Roo.data.DataReader = function(meta, recordType){
22056     
22057     this.meta = meta;
22058     
22059     this.recordType = recordType instanceof Array ? 
22060         Roo.data.Record.create(recordType) : recordType;
22061 };
22062
22063 Roo.data.DataReader.prototype = {
22064      /**
22065      * Create an empty record
22066      * @param {Object} data (optional) - overlay some values
22067      * @return {Roo.data.Record} record created.
22068      */
22069     newRow :  function(d) {
22070         var da =  {};
22071         this.recordType.prototype.fields.each(function(c) {
22072             switch( c.type) {
22073                 case 'int' : da[c.name] = 0; break;
22074                 case 'date' : da[c.name] = new Date(); break;
22075                 case 'float' : da[c.name] = 0.0; break;
22076                 case 'boolean' : da[c.name] = false; break;
22077                 default : da[c.name] = ""; break;
22078             }
22079             
22080         });
22081         return new this.recordType(Roo.apply(da, d));
22082     }
22083     
22084 };/*
22085  * Based on:
22086  * Ext JS Library 1.1.1
22087  * Copyright(c) 2006-2007, Ext JS, LLC.
22088  *
22089  * Originally Released Under LGPL - original licence link has changed is not relivant.
22090  *
22091  * Fork - LGPL
22092  * <script type="text/javascript">
22093  */
22094
22095 /**
22096  * @class Roo.data.DataProxy
22097  * @extends Roo.data.Observable
22098  * This class is an abstract base class for implementations which provide retrieval of
22099  * unformatted data objects.<br>
22100  * <p>
22101  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22102  * (of the appropriate type which knows how to parse the data object) to provide a block of
22103  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22104  * <p>
22105  * Custom implementations must implement the load method as described in
22106  * {@link Roo.data.HttpProxy#load}.
22107  */
22108 Roo.data.DataProxy = function(){
22109     this.addEvents({
22110         /**
22111          * @event beforeload
22112          * Fires before a network request is made to retrieve a data object.
22113          * @param {Object} This DataProxy object.
22114          * @param {Object} params The params parameter to the load function.
22115          */
22116         beforeload : true,
22117         /**
22118          * @event load
22119          * Fires before the load method's callback is called.
22120          * @param {Object} This DataProxy object.
22121          * @param {Object} o The data object.
22122          * @param {Object} arg The callback argument object passed to the load function.
22123          */
22124         load : true,
22125         /**
22126          * @event loadexception
22127          * Fires if an Exception occurs during data retrieval.
22128          * @param {Object} This DataProxy object.
22129          * @param {Object} o The data object.
22130          * @param {Object} arg The callback argument object passed to the load function.
22131          * @param {Object} e The Exception.
22132          */
22133         loadexception : true
22134     });
22135     Roo.data.DataProxy.superclass.constructor.call(this);
22136 };
22137
22138 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22139
22140     /**
22141      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22142      */
22143 /*
22144  * Based on:
22145  * Ext JS Library 1.1.1
22146  * Copyright(c) 2006-2007, Ext JS, LLC.
22147  *
22148  * Originally Released Under LGPL - original licence link has changed is not relivant.
22149  *
22150  * Fork - LGPL
22151  * <script type="text/javascript">
22152  */
22153 /**
22154  * @class Roo.data.MemoryProxy
22155  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22156  * to the Reader when its load method is called.
22157  * @constructor
22158  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22159  */
22160 Roo.data.MemoryProxy = function(data){
22161     if (data.data) {
22162         data = data.data;
22163     }
22164     Roo.data.MemoryProxy.superclass.constructor.call(this);
22165     this.data = data;
22166 };
22167
22168 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22169     /**
22170      * Load data from the requested source (in this case an in-memory
22171      * data object passed to the constructor), read the data object into
22172      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22173      * process that block using the passed callback.
22174      * @param {Object} params This parameter is not used by the MemoryProxy class.
22175      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22176      * object into a block of Roo.data.Records.
22177      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22178      * The function must be passed <ul>
22179      * <li>The Record block object</li>
22180      * <li>The "arg" argument from the load function</li>
22181      * <li>A boolean success indicator</li>
22182      * </ul>
22183      * @param {Object} scope The scope in which to call the callback
22184      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22185      */
22186     load : function(params, reader, callback, scope, arg){
22187         params = params || {};
22188         var result;
22189         try {
22190             result = reader.readRecords(this.data);
22191         }catch(e){
22192             this.fireEvent("loadexception", this, arg, null, e);
22193             callback.call(scope, null, arg, false);
22194             return;
22195         }
22196         callback.call(scope, result, arg, true);
22197     },
22198     
22199     // private
22200     update : function(params, records){
22201         
22202     }
22203 });/*
22204  * Based on:
22205  * Ext JS Library 1.1.1
22206  * Copyright(c) 2006-2007, Ext JS, LLC.
22207  *
22208  * Originally Released Under LGPL - original licence link has changed is not relivant.
22209  *
22210  * Fork - LGPL
22211  * <script type="text/javascript">
22212  */
22213 /**
22214  * @class Roo.data.HttpProxy
22215  * @extends Roo.data.DataProxy
22216  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22217  * configured to reference a certain URL.<br><br>
22218  * <p>
22219  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22220  * from which the running page was served.<br><br>
22221  * <p>
22222  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22223  * <p>
22224  * Be aware that to enable the browser to parse an XML document, the server must set
22225  * the Content-Type header in the HTTP response to "text/xml".
22226  * @constructor
22227  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22228  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22229  * will be used to make the request.
22230  */
22231 Roo.data.HttpProxy = function(conn){
22232     Roo.data.HttpProxy.superclass.constructor.call(this);
22233     // is conn a conn config or a real conn?
22234     this.conn = conn;
22235     this.useAjax = !conn || !conn.events;
22236   
22237 };
22238
22239 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22240     // thse are take from connection...
22241     
22242     /**
22243      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22244      */
22245     /**
22246      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22247      * extra parameters to each request made by this object. (defaults to undefined)
22248      */
22249     /**
22250      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22251      *  to each request made by this object. (defaults to undefined)
22252      */
22253     /**
22254      * @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)
22255      */
22256     /**
22257      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22258      */
22259      /**
22260      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22261      * @type Boolean
22262      */
22263   
22264
22265     /**
22266      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22267      * @type Boolean
22268      */
22269     /**
22270      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22271      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22272      * a finer-grained basis than the DataProxy events.
22273      */
22274     getConnection : function(){
22275         return this.useAjax ? Roo.Ajax : this.conn;
22276     },
22277
22278     /**
22279      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22280      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22281      * process that block using the passed callback.
22282      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22283      * for the request to the remote server.
22284      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22285      * object into a block of Roo.data.Records.
22286      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22287      * The function must be passed <ul>
22288      * <li>The Record block object</li>
22289      * <li>The "arg" argument from the load function</li>
22290      * <li>A boolean success indicator</li>
22291      * </ul>
22292      * @param {Object} scope The scope in which to call the callback
22293      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22294      */
22295     load : function(params, reader, callback, scope, arg){
22296         if(this.fireEvent("beforeload", this, params) !== false){
22297             var  o = {
22298                 params : params || {},
22299                 request: {
22300                     callback : callback,
22301                     scope : scope,
22302                     arg : arg
22303                 },
22304                 reader: reader,
22305                 callback : this.loadResponse,
22306                 scope: this
22307             };
22308             if(this.useAjax){
22309                 Roo.applyIf(o, this.conn);
22310                 if(this.activeRequest){
22311                     Roo.Ajax.abort(this.activeRequest);
22312                 }
22313                 this.activeRequest = Roo.Ajax.request(o);
22314             }else{
22315                 this.conn.request(o);
22316             }
22317         }else{
22318             callback.call(scope||this, null, arg, false);
22319         }
22320     },
22321
22322     // private
22323     loadResponse : function(o, success, response){
22324         delete this.activeRequest;
22325         if(!success){
22326             this.fireEvent("loadexception", this, o, response);
22327             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22328             return;
22329         }
22330         var result;
22331         try {
22332             result = o.reader.read(response);
22333         }catch(e){
22334             this.fireEvent("loadexception", this, o, response, e);
22335             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22336             return;
22337         }
22338         
22339         this.fireEvent("load", this, o, o.request.arg);
22340         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22341     },
22342
22343     // private
22344     update : function(dataSet){
22345
22346     },
22347
22348     // private
22349     updateResponse : function(dataSet){
22350
22351     }
22352 });/*
22353  * Based on:
22354  * Ext JS Library 1.1.1
22355  * Copyright(c) 2006-2007, Ext JS, LLC.
22356  *
22357  * Originally Released Under LGPL - original licence link has changed is not relivant.
22358  *
22359  * Fork - LGPL
22360  * <script type="text/javascript">
22361  */
22362
22363 /**
22364  * @class Roo.data.ScriptTagProxy
22365  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22366  * other than the originating domain of the running page.<br><br>
22367  * <p>
22368  * <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
22369  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22370  * <p>
22371  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22372  * source code that is used as the source inside a &lt;script> tag.<br><br>
22373  * <p>
22374  * In order for the browser to process the returned data, the server must wrap the data object
22375  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22376  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22377  * depending on whether the callback name was passed:
22378  * <p>
22379  * <pre><code>
22380 boolean scriptTag = false;
22381 String cb = request.getParameter("callback");
22382 if (cb != null) {
22383     scriptTag = true;
22384     response.setContentType("text/javascript");
22385 } else {
22386     response.setContentType("application/x-json");
22387 }
22388 Writer out = response.getWriter();
22389 if (scriptTag) {
22390     out.write(cb + "(");
22391 }
22392 out.print(dataBlock.toJsonString());
22393 if (scriptTag) {
22394     out.write(");");
22395 }
22396 </pre></code>
22397  *
22398  * @constructor
22399  * @param {Object} config A configuration object.
22400  */
22401 Roo.data.ScriptTagProxy = function(config){
22402     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22403     Roo.apply(this, config);
22404     this.head = document.getElementsByTagName("head")[0];
22405 };
22406
22407 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22408
22409 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22410     /**
22411      * @cfg {String} url The URL from which to request the data object.
22412      */
22413     /**
22414      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22415      */
22416     timeout : 30000,
22417     /**
22418      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22419      * the server the name of the callback function set up by the load call to process the returned data object.
22420      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22421      * javascript output which calls this named function passing the data object as its only parameter.
22422      */
22423     callbackParam : "callback",
22424     /**
22425      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22426      * name to the request.
22427      */
22428     nocache : true,
22429
22430     /**
22431      * Load data from the configured URL, read the data object into
22432      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22433      * process that block using the passed callback.
22434      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22435      * for the request to the remote server.
22436      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22437      * object into a block of Roo.data.Records.
22438      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22439      * The function must be passed <ul>
22440      * <li>The Record block object</li>
22441      * <li>The "arg" argument from the load function</li>
22442      * <li>A boolean success indicator</li>
22443      * </ul>
22444      * @param {Object} scope The scope in which to call the callback
22445      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22446      */
22447     load : function(params, reader, callback, scope, arg){
22448         if(this.fireEvent("beforeload", this, params) !== false){
22449
22450             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22451
22452             var url = this.url;
22453             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22454             if(this.nocache){
22455                 url += "&_dc=" + (new Date().getTime());
22456             }
22457             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22458             var trans = {
22459                 id : transId,
22460                 cb : "stcCallback"+transId,
22461                 scriptId : "stcScript"+transId,
22462                 params : params,
22463                 arg : arg,
22464                 url : url,
22465                 callback : callback,
22466                 scope : scope,
22467                 reader : reader
22468             };
22469             var conn = this;
22470
22471             window[trans.cb] = function(o){
22472                 conn.handleResponse(o, trans);
22473             };
22474
22475             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22476
22477             if(this.autoAbort !== false){
22478                 this.abort();
22479             }
22480
22481             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22482
22483             var script = document.createElement("script");
22484             script.setAttribute("src", url);
22485             script.setAttribute("type", "text/javascript");
22486             script.setAttribute("id", trans.scriptId);
22487             this.head.appendChild(script);
22488
22489             this.trans = trans;
22490         }else{
22491             callback.call(scope||this, null, arg, false);
22492         }
22493     },
22494
22495     // private
22496     isLoading : function(){
22497         return this.trans ? true : false;
22498     },
22499
22500     /**
22501      * Abort the current server request.
22502      */
22503     abort : function(){
22504         if(this.isLoading()){
22505             this.destroyTrans(this.trans);
22506         }
22507     },
22508
22509     // private
22510     destroyTrans : function(trans, isLoaded){
22511         this.head.removeChild(document.getElementById(trans.scriptId));
22512         clearTimeout(trans.timeoutId);
22513         if(isLoaded){
22514             window[trans.cb] = undefined;
22515             try{
22516                 delete window[trans.cb];
22517             }catch(e){}
22518         }else{
22519             // if hasn't been loaded, wait for load to remove it to prevent script error
22520             window[trans.cb] = function(){
22521                 window[trans.cb] = undefined;
22522                 try{
22523                     delete window[trans.cb];
22524                 }catch(e){}
22525             };
22526         }
22527     },
22528
22529     // private
22530     handleResponse : function(o, trans){
22531         this.trans = false;
22532         this.destroyTrans(trans, true);
22533         var result;
22534         try {
22535             result = trans.reader.readRecords(o);
22536         }catch(e){
22537             this.fireEvent("loadexception", this, o, trans.arg, e);
22538             trans.callback.call(trans.scope||window, null, trans.arg, false);
22539             return;
22540         }
22541         this.fireEvent("load", this, o, trans.arg);
22542         trans.callback.call(trans.scope||window, result, trans.arg, true);
22543     },
22544
22545     // private
22546     handleFailure : function(trans){
22547         this.trans = false;
22548         this.destroyTrans(trans, false);
22549         this.fireEvent("loadexception", this, null, trans.arg);
22550         trans.callback.call(trans.scope||window, null, trans.arg, false);
22551     }
22552 });/*
22553  * Based on:
22554  * Ext JS Library 1.1.1
22555  * Copyright(c) 2006-2007, Ext JS, LLC.
22556  *
22557  * Originally Released Under LGPL - original licence link has changed is not relivant.
22558  *
22559  * Fork - LGPL
22560  * <script type="text/javascript">
22561  */
22562
22563 /**
22564  * @class Roo.data.JsonReader
22565  * @extends Roo.data.DataReader
22566  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22567  * based on mappings in a provided Roo.data.Record constructor.
22568  * 
22569  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22570  * in the reply previously. 
22571  * 
22572  * <p>
22573  * Example code:
22574  * <pre><code>
22575 var RecordDef = Roo.data.Record.create([
22576     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22577     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22578 ]);
22579 var myReader = new Roo.data.JsonReader({
22580     totalProperty: "results",    // The property which contains the total dataset size (optional)
22581     root: "rows",                // The property which contains an Array of row objects
22582     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22583 }, RecordDef);
22584 </code></pre>
22585  * <p>
22586  * This would consume a JSON file like this:
22587  * <pre><code>
22588 { 'results': 2, 'rows': [
22589     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22590     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22591 }
22592 </code></pre>
22593  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22594  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22595  * paged from the remote server.
22596  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22597  * @cfg {String} root name of the property which contains the Array of row objects.
22598  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22599  * @constructor
22600  * Create a new JsonReader
22601  * @param {Object} meta Metadata configuration options
22602  * @param {Object} recordType Either an Array of field definition objects,
22603  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22604  */
22605 Roo.data.JsonReader = function(meta, recordType){
22606     
22607     meta = meta || {};
22608     // set some defaults:
22609     Roo.applyIf(meta, {
22610         totalProperty: 'total',
22611         successProperty : 'success',
22612         root : 'data',
22613         id : 'id'
22614     });
22615     
22616     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22617 };
22618 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22619     
22620     /**
22621      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22622      * Used by Store query builder to append _requestMeta to params.
22623      * 
22624      */
22625     metaFromRemote : false,
22626     /**
22627      * This method is only used by a DataProxy which has retrieved data from a remote server.
22628      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22629      * @return {Object} data A data block which is used by an Roo.data.Store object as
22630      * a cache of Roo.data.Records.
22631      */
22632     read : function(response){
22633         var json = response.responseText;
22634        
22635         var o = /* eval:var:o */ eval("("+json+")");
22636         if(!o) {
22637             throw {message: "JsonReader.read: Json object not found"};
22638         }
22639         
22640         if(o.metaData){
22641             
22642             delete this.ef;
22643             this.metaFromRemote = true;
22644             this.meta = o.metaData;
22645             this.recordType = Roo.data.Record.create(o.metaData.fields);
22646             this.onMetaChange(this.meta, this.recordType, o);
22647         }
22648         return this.readRecords(o);
22649     },
22650
22651     // private function a store will implement
22652     onMetaChange : function(meta, recordType, o){
22653
22654     },
22655
22656     /**
22657          * @ignore
22658          */
22659     simpleAccess: function(obj, subsc) {
22660         return obj[subsc];
22661     },
22662
22663         /**
22664          * @ignore
22665          */
22666     getJsonAccessor: function(){
22667         var re = /[\[\.]/;
22668         return function(expr) {
22669             try {
22670                 return(re.test(expr))
22671                     ? new Function("obj", "return obj." + expr)
22672                     : function(obj){
22673                         return obj[expr];
22674                     };
22675             } catch(e){}
22676             return Roo.emptyFn;
22677         };
22678     }(),
22679
22680     /**
22681      * Create a data block containing Roo.data.Records from an XML document.
22682      * @param {Object} o An object which contains an Array of row objects in the property specified
22683      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22684      * which contains the total size of the dataset.
22685      * @return {Object} data A data block which is used by an Roo.data.Store object as
22686      * a cache of Roo.data.Records.
22687      */
22688     readRecords : function(o){
22689         /**
22690          * After any data loads, the raw JSON data is available for further custom processing.
22691          * @type Object
22692          */
22693         this.o = o;
22694         var s = this.meta, Record = this.recordType,
22695             f = Record.prototype.fields, fi = f.items, fl = f.length;
22696
22697 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22698         if (!this.ef) {
22699             if(s.totalProperty) {
22700                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22701                 }
22702                 if(s.successProperty) {
22703                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22704                 }
22705                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22706                 if (s.id) {
22707                         var g = this.getJsonAccessor(s.id);
22708                         this.getId = function(rec) {
22709                                 var r = g(rec);  
22710                                 return (r === undefined || r === "") ? null : r;
22711                         };
22712                 } else {
22713                         this.getId = function(){return null;};
22714                 }
22715             this.ef = [];
22716             for(var jj = 0; jj < fl; jj++){
22717                 f = fi[jj];
22718                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22719                 this.ef[jj] = this.getJsonAccessor(map);
22720             }
22721         }
22722
22723         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22724         if(s.totalProperty){
22725             var vt = parseInt(this.getTotal(o), 10);
22726             if(!isNaN(vt)){
22727                 totalRecords = vt;
22728             }
22729         }
22730         if(s.successProperty){
22731             var vs = this.getSuccess(o);
22732             if(vs === false || vs === 'false'){
22733                 success = false;
22734             }
22735         }
22736         var records = [];
22737             for(var i = 0; i < c; i++){
22738                     var n = root[i];
22739                 var values = {};
22740                 var id = this.getId(n);
22741                 for(var j = 0; j < fl; j++){
22742                     f = fi[j];
22743                 var v = this.ef[j](n);
22744                 if (!f.convert) {
22745                     Roo.log('missing convert for ' + f.name);
22746                     Roo.log(f);
22747                     continue;
22748                 }
22749                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22750                 }
22751                 var record = new Record(values, id);
22752                 record.json = n;
22753                 records[i] = record;
22754             }
22755             return {
22756             raw : o,
22757                 success : success,
22758                 records : records,
22759                 totalRecords : totalRecords
22760             };
22761     }
22762 });/*
22763  * Based on:
22764  * Ext JS Library 1.1.1
22765  * Copyright(c) 2006-2007, Ext JS, LLC.
22766  *
22767  * Originally Released Under LGPL - original licence link has changed is not relivant.
22768  *
22769  * Fork - LGPL
22770  * <script type="text/javascript">
22771  */
22772
22773 /**
22774  * @class Roo.data.XmlReader
22775  * @extends Roo.data.DataReader
22776  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22777  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22778  * <p>
22779  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22780  * header in the HTTP response must be set to "text/xml".</em>
22781  * <p>
22782  * Example code:
22783  * <pre><code>
22784 var RecordDef = Roo.data.Record.create([
22785    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22786    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22787 ]);
22788 var myReader = new Roo.data.XmlReader({
22789    totalRecords: "results", // The element which contains the total dataset size (optional)
22790    record: "row",           // The repeated element which contains row information
22791    id: "id"                 // The element within the row that provides an ID for the record (optional)
22792 }, RecordDef);
22793 </code></pre>
22794  * <p>
22795  * This would consume an XML file like this:
22796  * <pre><code>
22797 &lt;?xml?>
22798 &lt;dataset>
22799  &lt;results>2&lt;/results>
22800  &lt;row>
22801    &lt;id>1&lt;/id>
22802    &lt;name>Bill&lt;/name>
22803    &lt;occupation>Gardener&lt;/occupation>
22804  &lt;/row>
22805  &lt;row>
22806    &lt;id>2&lt;/id>
22807    &lt;name>Ben&lt;/name>
22808    &lt;occupation>Horticulturalist&lt;/occupation>
22809  &lt;/row>
22810 &lt;/dataset>
22811 </code></pre>
22812  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22813  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22814  * paged from the remote server.
22815  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22816  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22817  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22818  * a record identifier value.
22819  * @constructor
22820  * Create a new XmlReader
22821  * @param {Object} meta Metadata configuration options
22822  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22823  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22824  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22825  */
22826 Roo.data.XmlReader = function(meta, recordType){
22827     meta = meta || {};
22828     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22829 };
22830 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22831     /**
22832      * This method is only used by a DataProxy which has retrieved data from a remote server.
22833          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22834          * to contain a method called 'responseXML' that returns an XML document object.
22835      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22836      * a cache of Roo.data.Records.
22837      */
22838     read : function(response){
22839         var doc = response.responseXML;
22840         if(!doc) {
22841             throw {message: "XmlReader.read: XML Document not available"};
22842         }
22843         return this.readRecords(doc);
22844     },
22845
22846     /**
22847      * Create a data block containing Roo.data.Records from an XML document.
22848          * @param {Object} doc A parsed XML document.
22849      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22850      * a cache of Roo.data.Records.
22851      */
22852     readRecords : function(doc){
22853         /**
22854          * After any data loads/reads, the raw XML Document is available for further custom processing.
22855          * @type XMLDocument
22856          */
22857         this.xmlData = doc;
22858         var root = doc.documentElement || doc;
22859         var q = Roo.DomQuery;
22860         var recordType = this.recordType, fields = recordType.prototype.fields;
22861         var sid = this.meta.id;
22862         var totalRecords = 0, success = true;
22863         if(this.meta.totalRecords){
22864             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22865         }
22866         
22867         if(this.meta.success){
22868             var sv = q.selectValue(this.meta.success, root, true);
22869             success = sv !== false && sv !== 'false';
22870         }
22871         var records = [];
22872         var ns = q.select(this.meta.record, root);
22873         for(var i = 0, len = ns.length; i < len; i++) {
22874                 var n = ns[i];
22875                 var values = {};
22876                 var id = sid ? q.selectValue(sid, n) : undefined;
22877                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22878                     var f = fields.items[j];
22879                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22880                     v = f.convert(v);
22881                     values[f.name] = v;
22882                 }
22883                 var record = new recordType(values, id);
22884                 record.node = n;
22885                 records[records.length] = record;
22886             }
22887
22888             return {
22889                 success : success,
22890                 records : records,
22891                 totalRecords : totalRecords || records.length
22892             };
22893     }
22894 });/*
22895  * Based on:
22896  * Ext JS Library 1.1.1
22897  * Copyright(c) 2006-2007, Ext JS, LLC.
22898  *
22899  * Originally Released Under LGPL - original licence link has changed is not relivant.
22900  *
22901  * Fork - LGPL
22902  * <script type="text/javascript">
22903  */
22904
22905 /**
22906  * @class Roo.data.ArrayReader
22907  * @extends Roo.data.DataReader
22908  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22909  * Each element of that Array represents a row of data fields. The
22910  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22911  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22912  * <p>
22913  * Example code:.
22914  * <pre><code>
22915 var RecordDef = Roo.data.Record.create([
22916     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22917     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22918 ]);
22919 var myReader = new Roo.data.ArrayReader({
22920     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22921 }, RecordDef);
22922 </code></pre>
22923  * <p>
22924  * This would consume an Array like this:
22925  * <pre><code>
22926 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22927   </code></pre>
22928  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22929  * @constructor
22930  * Create a new JsonReader
22931  * @param {Object} meta Metadata configuration options.
22932  * @param {Object} recordType Either an Array of field definition objects
22933  * as specified to {@link Roo.data.Record#create},
22934  * or an {@link Roo.data.Record} object
22935  * created using {@link Roo.data.Record#create}.
22936  */
22937 Roo.data.ArrayReader = function(meta, recordType){
22938     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22939 };
22940
22941 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22942     /**
22943      * Create a data block containing Roo.data.Records from an XML document.
22944      * @param {Object} o An Array of row objects which represents the dataset.
22945      * @return {Object} data A data block which is used by an Roo.data.Store object as
22946      * a cache of Roo.data.Records.
22947      */
22948     readRecords : function(o){
22949         var sid = this.meta ? this.meta.id : null;
22950         var recordType = this.recordType, fields = recordType.prototype.fields;
22951         var records = [];
22952         var root = o;
22953             for(var i = 0; i < root.length; i++){
22954                     var n = root[i];
22955                 var values = {};
22956                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22957                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22958                 var f = fields.items[j];
22959                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22960                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22961                 v = f.convert(v);
22962                 values[f.name] = v;
22963             }
22964                 var record = new recordType(values, id);
22965                 record.json = n;
22966                 records[records.length] = record;
22967             }
22968             return {
22969                 records : records,
22970                 totalRecords : records.length
22971             };
22972     }
22973 });/*
22974  * Based on:
22975  * Ext JS Library 1.1.1
22976  * Copyright(c) 2006-2007, Ext JS, LLC.
22977  *
22978  * Originally Released Under LGPL - original licence link has changed is not relivant.
22979  *
22980  * Fork - LGPL
22981  * <script type="text/javascript">
22982  */
22983
22984
22985 /**
22986  * @class Roo.data.Tree
22987  * @extends Roo.util.Observable
22988  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22989  * in the tree have most standard DOM functionality.
22990  * @constructor
22991  * @param {Node} root (optional) The root node
22992  */
22993 Roo.data.Tree = function(root){
22994    this.nodeHash = {};
22995    /**
22996     * The root node for this tree
22997     * @type Node
22998     */
22999    this.root = null;
23000    if(root){
23001        this.setRootNode(root);
23002    }
23003    this.addEvents({
23004        /**
23005         * @event append
23006         * Fires when a new child node is appended to a node in this tree.
23007         * @param {Tree} tree The owner tree
23008         * @param {Node} parent The parent node
23009         * @param {Node} node The newly appended node
23010         * @param {Number} index The index of the newly appended node
23011         */
23012        "append" : true,
23013        /**
23014         * @event remove
23015         * Fires when a child node is removed from a node in this tree.
23016         * @param {Tree} tree The owner tree
23017         * @param {Node} parent The parent node
23018         * @param {Node} node The child node removed
23019         */
23020        "remove" : true,
23021        /**
23022         * @event move
23023         * Fires when a node is moved to a new location in the tree
23024         * @param {Tree} tree The owner tree
23025         * @param {Node} node The node moved
23026         * @param {Node} oldParent The old parent of this node
23027         * @param {Node} newParent The new parent of this node
23028         * @param {Number} index The index it was moved to
23029         */
23030        "move" : true,
23031        /**
23032         * @event insert
23033         * Fires when a new child node is inserted in a node in this tree.
23034         * @param {Tree} tree The owner tree
23035         * @param {Node} parent The parent node
23036         * @param {Node} node The child node inserted
23037         * @param {Node} refNode The child node the node was inserted before
23038         */
23039        "insert" : true,
23040        /**
23041         * @event beforeappend
23042         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23043         * @param {Tree} tree The owner tree
23044         * @param {Node} parent The parent node
23045         * @param {Node} node The child node to be appended
23046         */
23047        "beforeappend" : true,
23048        /**
23049         * @event beforeremove
23050         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23051         * @param {Tree} tree The owner tree
23052         * @param {Node} parent The parent node
23053         * @param {Node} node The child node to be removed
23054         */
23055        "beforeremove" : true,
23056        /**
23057         * @event beforemove
23058         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23059         * @param {Tree} tree The owner tree
23060         * @param {Node} node The node being moved
23061         * @param {Node} oldParent The parent of the node
23062         * @param {Node} newParent The new parent the node is moving to
23063         * @param {Number} index The index it is being moved to
23064         */
23065        "beforemove" : true,
23066        /**
23067         * @event beforeinsert
23068         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} parent The parent node
23071         * @param {Node} node The child node to be inserted
23072         * @param {Node} refNode The child node the node is being inserted before
23073         */
23074        "beforeinsert" : true
23075    });
23076
23077     Roo.data.Tree.superclass.constructor.call(this);
23078 };
23079
23080 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23081     pathSeparator: "/",
23082
23083     proxyNodeEvent : function(){
23084         return this.fireEvent.apply(this, arguments);
23085     },
23086
23087     /**
23088      * Returns the root node for this tree.
23089      * @return {Node}
23090      */
23091     getRootNode : function(){
23092         return this.root;
23093     },
23094
23095     /**
23096      * Sets the root node for this tree.
23097      * @param {Node} node
23098      * @return {Node}
23099      */
23100     setRootNode : function(node){
23101         this.root = node;
23102         node.ownerTree = this;
23103         node.isRoot = true;
23104         this.registerNode(node);
23105         return node;
23106     },
23107
23108     /**
23109      * Gets a node in this tree by its id.
23110      * @param {String} id
23111      * @return {Node}
23112      */
23113     getNodeById : function(id){
23114         return this.nodeHash[id];
23115     },
23116
23117     registerNode : function(node){
23118         this.nodeHash[node.id] = node;
23119     },
23120
23121     unregisterNode : function(node){
23122         delete this.nodeHash[node.id];
23123     },
23124
23125     toString : function(){
23126         return "[Tree"+(this.id?" "+this.id:"")+"]";
23127     }
23128 });
23129
23130 /**
23131  * @class Roo.data.Node
23132  * @extends Roo.util.Observable
23133  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23134  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23135  * @constructor
23136  * @param {Object} attributes The attributes/config for the node
23137  */
23138 Roo.data.Node = function(attributes){
23139     /**
23140      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23141      * @type {Object}
23142      */
23143     this.attributes = attributes || {};
23144     this.leaf = this.attributes.leaf;
23145     /**
23146      * The node id. @type String
23147      */
23148     this.id = this.attributes.id;
23149     if(!this.id){
23150         this.id = Roo.id(null, "ynode-");
23151         this.attributes.id = this.id;
23152     }
23153      
23154     
23155     /**
23156      * All child nodes of this node. @type Array
23157      */
23158     this.childNodes = [];
23159     if(!this.childNodes.indexOf){ // indexOf is a must
23160         this.childNodes.indexOf = function(o){
23161             for(var i = 0, len = this.length; i < len; i++){
23162                 if(this[i] == o) {
23163                     return i;
23164                 }
23165             }
23166             return -1;
23167         };
23168     }
23169     /**
23170      * The parent node for this node. @type Node
23171      */
23172     this.parentNode = null;
23173     /**
23174      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23175      */
23176     this.firstChild = null;
23177     /**
23178      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23179      */
23180     this.lastChild = null;
23181     /**
23182      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23183      */
23184     this.previousSibling = null;
23185     /**
23186      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23187      */
23188     this.nextSibling = null;
23189
23190     this.addEvents({
23191        /**
23192         * @event append
23193         * Fires when a new child node is appended
23194         * @param {Tree} tree The owner tree
23195         * @param {Node} this This node
23196         * @param {Node} node The newly appended node
23197         * @param {Number} index The index of the newly appended node
23198         */
23199        "append" : true,
23200        /**
23201         * @event remove
23202         * Fires when a child node is removed
23203         * @param {Tree} tree The owner tree
23204         * @param {Node} this This node
23205         * @param {Node} node The removed node
23206         */
23207        "remove" : true,
23208        /**
23209         * @event move
23210         * Fires when this node is moved to a new location in the tree
23211         * @param {Tree} tree The owner tree
23212         * @param {Node} this This node
23213         * @param {Node} oldParent The old parent of this node
23214         * @param {Node} newParent The new parent of this node
23215         * @param {Number} index The index it was moved to
23216         */
23217        "move" : true,
23218        /**
23219         * @event insert
23220         * Fires when a new child node is inserted.
23221         * @param {Tree} tree The owner tree
23222         * @param {Node} this This node
23223         * @param {Node} node The child node inserted
23224         * @param {Node} refNode The child node the node was inserted before
23225         */
23226        "insert" : true,
23227        /**
23228         * @event beforeappend
23229         * Fires before a new child is appended, return false to cancel the append.
23230         * @param {Tree} tree The owner tree
23231         * @param {Node} this This node
23232         * @param {Node} node The child node to be appended
23233         */
23234        "beforeappend" : true,
23235        /**
23236         * @event beforeremove
23237         * Fires before a child is removed, return false to cancel the remove.
23238         * @param {Tree} tree The owner tree
23239         * @param {Node} this This node
23240         * @param {Node} node The child node to be removed
23241         */
23242        "beforeremove" : true,
23243        /**
23244         * @event beforemove
23245         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23246         * @param {Tree} tree The owner tree
23247         * @param {Node} this This node
23248         * @param {Node} oldParent The parent of this node
23249         * @param {Node} newParent The new parent this node is moving to
23250         * @param {Number} index The index it is being moved to
23251         */
23252        "beforemove" : true,
23253        /**
23254         * @event beforeinsert
23255         * Fires before a new child is inserted, return false to cancel the insert.
23256         * @param {Tree} tree The owner tree
23257         * @param {Node} this This node
23258         * @param {Node} node The child node to be inserted
23259         * @param {Node} refNode The child node the node is being inserted before
23260         */
23261        "beforeinsert" : true
23262    });
23263     this.listeners = this.attributes.listeners;
23264     Roo.data.Node.superclass.constructor.call(this);
23265 };
23266
23267 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23268     fireEvent : function(evtName){
23269         // first do standard event for this node
23270         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23271             return false;
23272         }
23273         // then bubble it up to the tree if the event wasn't cancelled
23274         var ot = this.getOwnerTree();
23275         if(ot){
23276             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23277                 return false;
23278             }
23279         }
23280         return true;
23281     },
23282
23283     /**
23284      * Returns true if this node is a leaf
23285      * @return {Boolean}
23286      */
23287     isLeaf : function(){
23288         return this.leaf === true;
23289     },
23290
23291     // private
23292     setFirstChild : function(node){
23293         this.firstChild = node;
23294     },
23295
23296     //private
23297     setLastChild : function(node){
23298         this.lastChild = node;
23299     },
23300
23301
23302     /**
23303      * Returns true if this node is the last child of its parent
23304      * @return {Boolean}
23305      */
23306     isLast : function(){
23307        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23308     },
23309
23310     /**
23311      * Returns true if this node is the first child of its parent
23312      * @return {Boolean}
23313      */
23314     isFirst : function(){
23315        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23316     },
23317
23318     hasChildNodes : function(){
23319         return !this.isLeaf() && this.childNodes.length > 0;
23320     },
23321
23322     /**
23323      * Insert node(s) as the last child node of this node.
23324      * @param {Node/Array} node The node or Array of nodes to append
23325      * @return {Node} The appended node if single append, or null if an array was passed
23326      */
23327     appendChild : function(node){
23328         var multi = false;
23329         if(node instanceof Array){
23330             multi = node;
23331         }else if(arguments.length > 1){
23332             multi = arguments;
23333         }
23334         // if passed an array or multiple args do them one by one
23335         if(multi){
23336             for(var i = 0, len = multi.length; i < len; i++) {
23337                 this.appendChild(multi[i]);
23338             }
23339         }else{
23340             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23341                 return false;
23342             }
23343             var index = this.childNodes.length;
23344             var oldParent = node.parentNode;
23345             // it's a move, make sure we move it cleanly
23346             if(oldParent){
23347                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23348                     return false;
23349                 }
23350                 oldParent.removeChild(node);
23351             }
23352             index = this.childNodes.length;
23353             if(index == 0){
23354                 this.setFirstChild(node);
23355             }
23356             this.childNodes.push(node);
23357             node.parentNode = this;
23358             var ps = this.childNodes[index-1];
23359             if(ps){
23360                 node.previousSibling = ps;
23361                 ps.nextSibling = node;
23362             }else{
23363                 node.previousSibling = null;
23364             }
23365             node.nextSibling = null;
23366             this.setLastChild(node);
23367             node.setOwnerTree(this.getOwnerTree());
23368             this.fireEvent("append", this.ownerTree, this, node, index);
23369             if(oldParent){
23370                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23371             }
23372             return node;
23373         }
23374     },
23375
23376     /**
23377      * Removes a child node from this node.
23378      * @param {Node} node The node to remove
23379      * @return {Node} The removed node
23380      */
23381     removeChild : function(node){
23382         var index = this.childNodes.indexOf(node);
23383         if(index == -1){
23384             return false;
23385         }
23386         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23387             return false;
23388         }
23389
23390         // remove it from childNodes collection
23391         this.childNodes.splice(index, 1);
23392
23393         // update siblings
23394         if(node.previousSibling){
23395             node.previousSibling.nextSibling = node.nextSibling;
23396         }
23397         if(node.nextSibling){
23398             node.nextSibling.previousSibling = node.previousSibling;
23399         }
23400
23401         // update child refs
23402         if(this.firstChild == node){
23403             this.setFirstChild(node.nextSibling);
23404         }
23405         if(this.lastChild == node){
23406             this.setLastChild(node.previousSibling);
23407         }
23408
23409         node.setOwnerTree(null);
23410         // clear any references from the node
23411         node.parentNode = null;
23412         node.previousSibling = null;
23413         node.nextSibling = null;
23414         this.fireEvent("remove", this.ownerTree, this, node);
23415         return node;
23416     },
23417
23418     /**
23419      * Inserts the first node before the second node in this nodes childNodes collection.
23420      * @param {Node} node The node to insert
23421      * @param {Node} refNode The node to insert before (if null the node is appended)
23422      * @return {Node} The inserted node
23423      */
23424     insertBefore : function(node, refNode){
23425         if(!refNode){ // like standard Dom, refNode can be null for append
23426             return this.appendChild(node);
23427         }
23428         // nothing to do
23429         if(node == refNode){
23430             return false;
23431         }
23432
23433         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23434             return false;
23435         }
23436         var index = this.childNodes.indexOf(refNode);
23437         var oldParent = node.parentNode;
23438         var refIndex = index;
23439
23440         // when moving internally, indexes will change after remove
23441         if(oldParent == this && this.childNodes.indexOf(node) < index){
23442             refIndex--;
23443         }
23444
23445         // it's a move, make sure we move it cleanly
23446         if(oldParent){
23447             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23448                 return false;
23449             }
23450             oldParent.removeChild(node);
23451         }
23452         if(refIndex == 0){
23453             this.setFirstChild(node);
23454         }
23455         this.childNodes.splice(refIndex, 0, node);
23456         node.parentNode = this;
23457         var ps = this.childNodes[refIndex-1];
23458         if(ps){
23459             node.previousSibling = ps;
23460             ps.nextSibling = node;
23461         }else{
23462             node.previousSibling = null;
23463         }
23464         node.nextSibling = refNode;
23465         refNode.previousSibling = node;
23466         node.setOwnerTree(this.getOwnerTree());
23467         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23468         if(oldParent){
23469             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23470         }
23471         return node;
23472     },
23473
23474     /**
23475      * Returns the child node at the specified index.
23476      * @param {Number} index
23477      * @return {Node}
23478      */
23479     item : function(index){
23480         return this.childNodes[index];
23481     },
23482
23483     /**
23484      * Replaces one child node in this node with another.
23485      * @param {Node} newChild The replacement node
23486      * @param {Node} oldChild The node to replace
23487      * @return {Node} The replaced node
23488      */
23489     replaceChild : function(newChild, oldChild){
23490         this.insertBefore(newChild, oldChild);
23491         this.removeChild(oldChild);
23492         return oldChild;
23493     },
23494
23495     /**
23496      * Returns the index of a child node
23497      * @param {Node} node
23498      * @return {Number} The index of the node or -1 if it was not found
23499      */
23500     indexOf : function(child){
23501         return this.childNodes.indexOf(child);
23502     },
23503
23504     /**
23505      * Returns the tree this node is in.
23506      * @return {Tree}
23507      */
23508     getOwnerTree : function(){
23509         // if it doesn't have one, look for one
23510         if(!this.ownerTree){
23511             var p = this;
23512             while(p){
23513                 if(p.ownerTree){
23514                     this.ownerTree = p.ownerTree;
23515                     break;
23516                 }
23517                 p = p.parentNode;
23518             }
23519         }
23520         return this.ownerTree;
23521     },
23522
23523     /**
23524      * Returns depth of this node (the root node has a depth of 0)
23525      * @return {Number}
23526      */
23527     getDepth : function(){
23528         var depth = 0;
23529         var p = this;
23530         while(p.parentNode){
23531             ++depth;
23532             p = p.parentNode;
23533         }
23534         return depth;
23535     },
23536
23537     // private
23538     setOwnerTree : function(tree){
23539         // if it's move, we need to update everyone
23540         if(tree != this.ownerTree){
23541             if(this.ownerTree){
23542                 this.ownerTree.unregisterNode(this);
23543             }
23544             this.ownerTree = tree;
23545             var cs = this.childNodes;
23546             for(var i = 0, len = cs.length; i < len; i++) {
23547                 cs[i].setOwnerTree(tree);
23548             }
23549             if(tree){
23550                 tree.registerNode(this);
23551             }
23552         }
23553     },
23554
23555     /**
23556      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23557      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23558      * @return {String} The path
23559      */
23560     getPath : function(attr){
23561         attr = attr || "id";
23562         var p = this.parentNode;
23563         var b = [this.attributes[attr]];
23564         while(p){
23565             b.unshift(p.attributes[attr]);
23566             p = p.parentNode;
23567         }
23568         var sep = this.getOwnerTree().pathSeparator;
23569         return sep + b.join(sep);
23570     },
23571
23572     /**
23573      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23574      * function call will be the scope provided or the current node. The arguments to the function
23575      * will be the args provided or the current node. If the function returns false at any point,
23576      * the bubble is stopped.
23577      * @param {Function} fn The function to call
23578      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23579      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23580      */
23581     bubble : function(fn, scope, args){
23582         var p = this;
23583         while(p){
23584             if(fn.call(scope || p, args || p) === false){
23585                 break;
23586             }
23587             p = p.parentNode;
23588         }
23589     },
23590
23591     /**
23592      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23593      * function call will be the scope provided or the current node. The arguments to the function
23594      * will be the args provided or the current node. If the function returns false at any point,
23595      * the cascade is stopped on that branch.
23596      * @param {Function} fn The function to call
23597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23599      */
23600     cascade : function(fn, scope, args){
23601         if(fn.call(scope || this, args || this) !== false){
23602             var cs = this.childNodes;
23603             for(var i = 0, len = cs.length; i < len; i++) {
23604                 cs[i].cascade(fn, scope, args);
23605             }
23606         }
23607     },
23608
23609     /**
23610      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23611      * function call will be the scope provided or the current node. The arguments to the function
23612      * will be the args provided or the current node. If the function returns false at any point,
23613      * the iteration stops.
23614      * @param {Function} fn The function to call
23615      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23616      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23617      */
23618     eachChild : function(fn, scope, args){
23619         var cs = this.childNodes;
23620         for(var i = 0, len = cs.length; i < len; i++) {
23621                 if(fn.call(scope || this, args || cs[i]) === false){
23622                     break;
23623                 }
23624         }
23625     },
23626
23627     /**
23628      * Finds the first child that has the attribute with the specified value.
23629      * @param {String} attribute The attribute name
23630      * @param {Mixed} value The value to search for
23631      * @return {Node} The found child or null if none was found
23632      */
23633     findChild : function(attribute, value){
23634         var cs = this.childNodes;
23635         for(var i = 0, len = cs.length; i < len; i++) {
23636                 if(cs[i].attributes[attribute] == value){
23637                     return cs[i];
23638                 }
23639         }
23640         return null;
23641     },
23642
23643     /**
23644      * Finds the first child by a custom function. The child matches if the function passed
23645      * returns true.
23646      * @param {Function} fn
23647      * @param {Object} scope (optional)
23648      * @return {Node} The found child or null if none was found
23649      */
23650     findChildBy : function(fn, scope){
23651         var cs = this.childNodes;
23652         for(var i = 0, len = cs.length; i < len; i++) {
23653                 if(fn.call(scope||cs[i], cs[i]) === true){
23654                     return cs[i];
23655                 }
23656         }
23657         return null;
23658     },
23659
23660     /**
23661      * Sorts this nodes children using the supplied sort function
23662      * @param {Function} fn
23663      * @param {Object} scope (optional)
23664      */
23665     sort : function(fn, scope){
23666         var cs = this.childNodes;
23667         var len = cs.length;
23668         if(len > 0){
23669             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23670             cs.sort(sortFn);
23671             for(var i = 0; i < len; i++){
23672                 var n = cs[i];
23673                 n.previousSibling = cs[i-1];
23674                 n.nextSibling = cs[i+1];
23675                 if(i == 0){
23676                     this.setFirstChild(n);
23677                 }
23678                 if(i == len-1){
23679                     this.setLastChild(n);
23680                 }
23681             }
23682         }
23683     },
23684
23685     /**
23686      * Returns true if this node is an ancestor (at any point) of the passed node.
23687      * @param {Node} node
23688      * @return {Boolean}
23689      */
23690     contains : function(node){
23691         return node.isAncestor(this);
23692     },
23693
23694     /**
23695      * Returns true if the passed node is an ancestor (at any point) of this node.
23696      * @param {Node} node
23697      * @return {Boolean}
23698      */
23699     isAncestor : function(node){
23700         var p = this.parentNode;
23701         while(p){
23702             if(p == node){
23703                 return true;
23704             }
23705             p = p.parentNode;
23706         }
23707         return false;
23708     },
23709
23710     toString : function(){
23711         return "[Node"+(this.id?" "+this.id:"")+"]";
23712     }
23713 });/*
23714  * Based on:
23715  * Ext JS Library 1.1.1
23716  * Copyright(c) 2006-2007, Ext JS, LLC.
23717  *
23718  * Originally Released Under LGPL - original licence link has changed is not relivant.
23719  *
23720  * Fork - LGPL
23721  * <script type="text/javascript">
23722  */
23723  (function(){ 
23724 /**
23725  * @class Roo.Layer
23726  * @extends Roo.Element
23727  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23728  * automatic maintaining of shadow/shim positions.
23729  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23730  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23731  * you can pass a string with a CSS class name. False turns off the shadow.
23732  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23733  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23734  * @cfg {String} cls CSS class to add to the element
23735  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23736  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23737  * @constructor
23738  * @param {Object} config An object with config options.
23739  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23740  */
23741
23742 Roo.Layer = function(config, existingEl){
23743     config = config || {};
23744     var dh = Roo.DomHelper;
23745     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23746     if(existingEl){
23747         this.dom = Roo.getDom(existingEl);
23748     }
23749     if(!this.dom){
23750         var o = config.dh || {tag: "div", cls: "x-layer"};
23751         this.dom = dh.append(pel, o);
23752     }
23753     if(config.cls){
23754         this.addClass(config.cls);
23755     }
23756     this.constrain = config.constrain !== false;
23757     this.visibilityMode = Roo.Element.VISIBILITY;
23758     if(config.id){
23759         this.id = this.dom.id = config.id;
23760     }else{
23761         this.id = Roo.id(this.dom);
23762     }
23763     this.zindex = config.zindex || this.getZIndex();
23764     this.position("absolute", this.zindex);
23765     if(config.shadow){
23766         this.shadowOffset = config.shadowOffset || 4;
23767         this.shadow = new Roo.Shadow({
23768             offset : this.shadowOffset,
23769             mode : config.shadow
23770         });
23771     }else{
23772         this.shadowOffset = 0;
23773     }
23774     this.useShim = config.shim !== false && Roo.useShims;
23775     this.useDisplay = config.useDisplay;
23776     this.hide();
23777 };
23778
23779 var supr = Roo.Element.prototype;
23780
23781 // shims are shared among layer to keep from having 100 iframes
23782 var shims = [];
23783
23784 Roo.extend(Roo.Layer, Roo.Element, {
23785
23786     getZIndex : function(){
23787         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23788     },
23789
23790     getShim : function(){
23791         if(!this.useShim){
23792             return null;
23793         }
23794         if(this.shim){
23795             return this.shim;
23796         }
23797         var shim = shims.shift();
23798         if(!shim){
23799             shim = this.createShim();
23800             shim.enableDisplayMode('block');
23801             shim.dom.style.display = 'none';
23802             shim.dom.style.visibility = 'visible';
23803         }
23804         var pn = this.dom.parentNode;
23805         if(shim.dom.parentNode != pn){
23806             pn.insertBefore(shim.dom, this.dom);
23807         }
23808         shim.setStyle('z-index', this.getZIndex()-2);
23809         this.shim = shim;
23810         return shim;
23811     },
23812
23813     hideShim : function(){
23814         if(this.shim){
23815             this.shim.setDisplayed(false);
23816             shims.push(this.shim);
23817             delete this.shim;
23818         }
23819     },
23820
23821     disableShadow : function(){
23822         if(this.shadow){
23823             this.shadowDisabled = true;
23824             this.shadow.hide();
23825             this.lastShadowOffset = this.shadowOffset;
23826             this.shadowOffset = 0;
23827         }
23828     },
23829
23830     enableShadow : function(show){
23831         if(this.shadow){
23832             this.shadowDisabled = false;
23833             this.shadowOffset = this.lastShadowOffset;
23834             delete this.lastShadowOffset;
23835             if(show){
23836                 this.sync(true);
23837             }
23838         }
23839     },
23840
23841     // private
23842     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23843     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23844     sync : function(doShow){
23845         var sw = this.shadow;
23846         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23847             var sh = this.getShim();
23848
23849             var w = this.getWidth(),
23850                 h = this.getHeight();
23851
23852             var l = this.getLeft(true),
23853                 t = this.getTop(true);
23854
23855             if(sw && !this.shadowDisabled){
23856                 if(doShow && !sw.isVisible()){
23857                     sw.show(this);
23858                 }else{
23859                     sw.realign(l, t, w, h);
23860                 }
23861                 if(sh){
23862                     if(doShow){
23863                        sh.show();
23864                     }
23865                     // fit the shim behind the shadow, so it is shimmed too
23866                     var a = sw.adjusts, s = sh.dom.style;
23867                     s.left = (Math.min(l, l+a.l))+"px";
23868                     s.top = (Math.min(t, t+a.t))+"px";
23869                     s.width = (w+a.w)+"px";
23870                     s.height = (h+a.h)+"px";
23871                 }
23872             }else if(sh){
23873                 if(doShow){
23874                    sh.show();
23875                 }
23876                 sh.setSize(w, h);
23877                 sh.setLeftTop(l, t);
23878             }
23879             
23880         }
23881     },
23882
23883     // private
23884     destroy : function(){
23885         this.hideShim();
23886         if(this.shadow){
23887             this.shadow.hide();
23888         }
23889         this.removeAllListeners();
23890         var pn = this.dom.parentNode;
23891         if(pn){
23892             pn.removeChild(this.dom);
23893         }
23894         Roo.Element.uncache(this.id);
23895     },
23896
23897     remove : function(){
23898         this.destroy();
23899     },
23900
23901     // private
23902     beginUpdate : function(){
23903         this.updating = true;
23904     },
23905
23906     // private
23907     endUpdate : function(){
23908         this.updating = false;
23909         this.sync(true);
23910     },
23911
23912     // private
23913     hideUnders : function(negOffset){
23914         if(this.shadow){
23915             this.shadow.hide();
23916         }
23917         this.hideShim();
23918     },
23919
23920     // private
23921     constrainXY : function(){
23922         if(this.constrain){
23923             var vw = Roo.lib.Dom.getViewWidth(),
23924                 vh = Roo.lib.Dom.getViewHeight();
23925             var s = Roo.get(document).getScroll();
23926
23927             var xy = this.getXY();
23928             var x = xy[0], y = xy[1];   
23929             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23930             // only move it if it needs it
23931             var moved = false;
23932             // first validate right/bottom
23933             if((x + w) > vw+s.left){
23934                 x = vw - w - this.shadowOffset;
23935                 moved = true;
23936             }
23937             if((y + h) > vh+s.top){
23938                 y = vh - h - this.shadowOffset;
23939                 moved = true;
23940             }
23941             // then make sure top/left isn't negative
23942             if(x < s.left){
23943                 x = s.left;
23944                 moved = true;
23945             }
23946             if(y < s.top){
23947                 y = s.top;
23948                 moved = true;
23949             }
23950             if(moved){
23951                 if(this.avoidY){
23952                     var ay = this.avoidY;
23953                     if(y <= ay && (y+h) >= ay){
23954                         y = ay-h-5;   
23955                     }
23956                 }
23957                 xy = [x, y];
23958                 this.storeXY(xy);
23959                 supr.setXY.call(this, xy);
23960                 this.sync();
23961             }
23962         }
23963     },
23964
23965     isVisible : function(){
23966         return this.visible;    
23967     },
23968
23969     // private
23970     showAction : function(){
23971         this.visible = true; // track visibility to prevent getStyle calls
23972         if(this.useDisplay === true){
23973             this.setDisplayed("");
23974         }else if(this.lastXY){
23975             supr.setXY.call(this, this.lastXY);
23976         }else if(this.lastLT){
23977             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23978         }
23979     },
23980
23981     // private
23982     hideAction : function(){
23983         this.visible = false;
23984         if(this.useDisplay === true){
23985             this.setDisplayed(false);
23986         }else{
23987             this.setLeftTop(-10000,-10000);
23988         }
23989     },
23990
23991     // overridden Element method
23992     setVisible : function(v, a, d, c, e){
23993         if(v){
23994             this.showAction();
23995         }
23996         if(a && v){
23997             var cb = function(){
23998                 this.sync(true);
23999                 if(c){
24000                     c();
24001                 }
24002             }.createDelegate(this);
24003             supr.setVisible.call(this, true, true, d, cb, e);
24004         }else{
24005             if(!v){
24006                 this.hideUnders(true);
24007             }
24008             var cb = c;
24009             if(a){
24010                 cb = function(){
24011                     this.hideAction();
24012                     if(c){
24013                         c();
24014                     }
24015                 }.createDelegate(this);
24016             }
24017             supr.setVisible.call(this, v, a, d, cb, e);
24018             if(v){
24019                 this.sync(true);
24020             }else if(!a){
24021                 this.hideAction();
24022             }
24023         }
24024     },
24025
24026     storeXY : function(xy){
24027         delete this.lastLT;
24028         this.lastXY = xy;
24029     },
24030
24031     storeLeftTop : function(left, top){
24032         delete this.lastXY;
24033         this.lastLT = [left, top];
24034     },
24035
24036     // private
24037     beforeFx : function(){
24038         this.beforeAction();
24039         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24040     },
24041
24042     // private
24043     afterFx : function(){
24044         Roo.Layer.superclass.afterFx.apply(this, arguments);
24045         this.sync(this.isVisible());
24046     },
24047
24048     // private
24049     beforeAction : function(){
24050         if(!this.updating && this.shadow){
24051             this.shadow.hide();
24052         }
24053     },
24054
24055     // overridden Element method
24056     setLeft : function(left){
24057         this.storeLeftTop(left, this.getTop(true));
24058         supr.setLeft.apply(this, arguments);
24059         this.sync();
24060     },
24061
24062     setTop : function(top){
24063         this.storeLeftTop(this.getLeft(true), top);
24064         supr.setTop.apply(this, arguments);
24065         this.sync();
24066     },
24067
24068     setLeftTop : function(left, top){
24069         this.storeLeftTop(left, top);
24070         supr.setLeftTop.apply(this, arguments);
24071         this.sync();
24072     },
24073
24074     setXY : function(xy, a, d, c, e){
24075         this.fixDisplay();
24076         this.beforeAction();
24077         this.storeXY(xy);
24078         var cb = this.createCB(c);
24079         supr.setXY.call(this, xy, a, d, cb, e);
24080         if(!a){
24081             cb();
24082         }
24083     },
24084
24085     // private
24086     createCB : function(c){
24087         var el = this;
24088         return function(){
24089             el.constrainXY();
24090             el.sync(true);
24091             if(c){
24092                 c();
24093             }
24094         };
24095     },
24096
24097     // overridden Element method
24098     setX : function(x, a, d, c, e){
24099         this.setXY([x, this.getY()], a, d, c, e);
24100     },
24101
24102     // overridden Element method
24103     setY : function(y, a, d, c, e){
24104         this.setXY([this.getX(), y], a, d, c, e);
24105     },
24106
24107     // overridden Element method
24108     setSize : function(w, h, a, d, c, e){
24109         this.beforeAction();
24110         var cb = this.createCB(c);
24111         supr.setSize.call(this, w, h, a, d, cb, e);
24112         if(!a){
24113             cb();
24114         }
24115     },
24116
24117     // overridden Element method
24118     setWidth : function(w, a, d, c, e){
24119         this.beforeAction();
24120         var cb = this.createCB(c);
24121         supr.setWidth.call(this, w, a, d, cb, e);
24122         if(!a){
24123             cb();
24124         }
24125     },
24126
24127     // overridden Element method
24128     setHeight : function(h, a, d, c, e){
24129         this.beforeAction();
24130         var cb = this.createCB(c);
24131         supr.setHeight.call(this, h, a, d, cb, e);
24132         if(!a){
24133             cb();
24134         }
24135     },
24136
24137     // overridden Element method
24138     setBounds : function(x, y, w, h, a, d, c, e){
24139         this.beforeAction();
24140         var cb = this.createCB(c);
24141         if(!a){
24142             this.storeXY([x, y]);
24143             supr.setXY.call(this, [x, y]);
24144             supr.setSize.call(this, w, h, a, d, cb, e);
24145             cb();
24146         }else{
24147             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24148         }
24149         return this;
24150     },
24151     
24152     /**
24153      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24154      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24155      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24156      * @param {Number} zindex The new z-index to set
24157      * @return {this} The Layer
24158      */
24159     setZIndex : function(zindex){
24160         this.zindex = zindex;
24161         this.setStyle("z-index", zindex + 2);
24162         if(this.shadow){
24163             this.shadow.setZIndex(zindex + 1);
24164         }
24165         if(this.shim){
24166             this.shim.setStyle("z-index", zindex);
24167         }
24168     }
24169 });
24170 })();/*
24171  * Based on:
24172  * Ext JS Library 1.1.1
24173  * Copyright(c) 2006-2007, Ext JS, LLC.
24174  *
24175  * Originally Released Under LGPL - original licence link has changed is not relivant.
24176  *
24177  * Fork - LGPL
24178  * <script type="text/javascript">
24179  */
24180
24181
24182 /**
24183  * @class Roo.Shadow
24184  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24185  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24186  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24187  * @constructor
24188  * Create a new Shadow
24189  * @param {Object} config The config object
24190  */
24191 Roo.Shadow = function(config){
24192     Roo.apply(this, config);
24193     if(typeof this.mode != "string"){
24194         this.mode = this.defaultMode;
24195     }
24196     var o = this.offset, a = {h: 0};
24197     var rad = Math.floor(this.offset/2);
24198     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24199         case "drop":
24200             a.w = 0;
24201             a.l = a.t = o;
24202             a.t -= 1;
24203             if(Roo.isIE){
24204                 a.l -= this.offset + rad;
24205                 a.t -= this.offset + rad;
24206                 a.w -= rad;
24207                 a.h -= rad;
24208                 a.t += 1;
24209             }
24210         break;
24211         case "sides":
24212             a.w = (o*2);
24213             a.l = -o;
24214             a.t = o-1;
24215             if(Roo.isIE){
24216                 a.l -= (this.offset - rad);
24217                 a.t -= this.offset + rad;
24218                 a.l += 1;
24219                 a.w -= (this.offset - rad)*2;
24220                 a.w -= rad + 1;
24221                 a.h -= 1;
24222             }
24223         break;
24224         case "frame":
24225             a.w = a.h = (o*2);
24226             a.l = a.t = -o;
24227             a.t += 1;
24228             a.h -= 2;
24229             if(Roo.isIE){
24230                 a.l -= (this.offset - rad);
24231                 a.t -= (this.offset - rad);
24232                 a.l += 1;
24233                 a.w -= (this.offset + rad + 1);
24234                 a.h -= (this.offset + rad);
24235                 a.h += 1;
24236             }
24237         break;
24238     };
24239
24240     this.adjusts = a;
24241 };
24242
24243 Roo.Shadow.prototype = {
24244     /**
24245      * @cfg {String} mode
24246      * The shadow display mode.  Supports the following options:<br />
24247      * sides: Shadow displays on both sides and bottom only<br />
24248      * frame: Shadow displays equally on all four sides<br />
24249      * drop: Traditional bottom-right drop shadow (default)
24250      */
24251     /**
24252      * @cfg {String} offset
24253      * The number of pixels to offset the shadow from the element (defaults to 4)
24254      */
24255     offset: 4,
24256
24257     // private
24258     defaultMode: "drop",
24259
24260     /**
24261      * Displays the shadow under the target element
24262      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24263      */
24264     show : function(target){
24265         target = Roo.get(target);
24266         if(!this.el){
24267             this.el = Roo.Shadow.Pool.pull();
24268             if(this.el.dom.nextSibling != target.dom){
24269                 this.el.insertBefore(target);
24270             }
24271         }
24272         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24273         if(Roo.isIE){
24274             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24275         }
24276         this.realign(
24277             target.getLeft(true),
24278             target.getTop(true),
24279             target.getWidth(),
24280             target.getHeight()
24281         );
24282         this.el.dom.style.display = "block";
24283     },
24284
24285     /**
24286      * Returns true if the shadow is visible, else false
24287      */
24288     isVisible : function(){
24289         return this.el ? true : false;  
24290     },
24291
24292     /**
24293      * Direct alignment when values are already available. Show must be called at least once before
24294      * calling this method to ensure it is initialized.
24295      * @param {Number} left The target element left position
24296      * @param {Number} top The target element top position
24297      * @param {Number} width The target element width
24298      * @param {Number} height The target element height
24299      */
24300     realign : function(l, t, w, h){
24301         if(!this.el){
24302             return;
24303         }
24304         var a = this.adjusts, d = this.el.dom, s = d.style;
24305         var iea = 0;
24306         s.left = (l+a.l)+"px";
24307         s.top = (t+a.t)+"px";
24308         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24309  
24310         if(s.width != sws || s.height != shs){
24311             s.width = sws;
24312             s.height = shs;
24313             if(!Roo.isIE){
24314                 var cn = d.childNodes;
24315                 var sww = Math.max(0, (sw-12))+"px";
24316                 cn[0].childNodes[1].style.width = sww;
24317                 cn[1].childNodes[1].style.width = sww;
24318                 cn[2].childNodes[1].style.width = sww;
24319                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24320             }
24321         }
24322     },
24323
24324     /**
24325      * Hides this shadow
24326      */
24327     hide : function(){
24328         if(this.el){
24329             this.el.dom.style.display = "none";
24330             Roo.Shadow.Pool.push(this.el);
24331             delete this.el;
24332         }
24333     },
24334
24335     /**
24336      * Adjust the z-index of this shadow
24337      * @param {Number} zindex The new z-index
24338      */
24339     setZIndex : function(z){
24340         this.zIndex = z;
24341         if(this.el){
24342             this.el.setStyle("z-index", z);
24343         }
24344     }
24345 };
24346
24347 // Private utility class that manages the internal Shadow cache
24348 Roo.Shadow.Pool = function(){
24349     var p = [];
24350     var markup = Roo.isIE ?
24351                  '<div class="x-ie-shadow"></div>' :
24352                  '<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>';
24353     return {
24354         pull : function(){
24355             var sh = p.shift();
24356             if(!sh){
24357                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24358                 sh.autoBoxAdjust = false;
24359             }
24360             return sh;
24361         },
24362
24363         push : function(sh){
24364             p.push(sh);
24365         }
24366     };
24367 }();/*
24368  * Based on:
24369  * Ext JS Library 1.1.1
24370  * Copyright(c) 2006-2007, Ext JS, LLC.
24371  *
24372  * Originally Released Under LGPL - original licence link has changed is not relivant.
24373  *
24374  * Fork - LGPL
24375  * <script type="text/javascript">
24376  */
24377
24378
24379 /**
24380  * @class Roo.SplitBar
24381  * @extends Roo.util.Observable
24382  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24383  * <br><br>
24384  * Usage:
24385  * <pre><code>
24386 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24387                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24388 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24389 split.minSize = 100;
24390 split.maxSize = 600;
24391 split.animate = true;
24392 split.on('moved', splitterMoved);
24393 </code></pre>
24394  * @constructor
24395  * Create a new SplitBar
24396  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24397  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24398  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24399  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24400                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24401                         position of the SplitBar).
24402  */
24403 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24404     
24405     /** @private */
24406     this.el = Roo.get(dragElement, true);
24407     this.el.dom.unselectable = "on";
24408     /** @private */
24409     this.resizingEl = Roo.get(resizingElement, true);
24410
24411     /**
24412      * @private
24413      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24414      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24415      * @type Number
24416      */
24417     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24418     
24419     /**
24420      * The minimum size of the resizing element. (Defaults to 0)
24421      * @type Number
24422      */
24423     this.minSize = 0;
24424     
24425     /**
24426      * The maximum size of the resizing element. (Defaults to 2000)
24427      * @type Number
24428      */
24429     this.maxSize = 2000;
24430     
24431     /**
24432      * Whether to animate the transition to the new size
24433      * @type Boolean
24434      */
24435     this.animate = false;
24436     
24437     /**
24438      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24439      * @type Boolean
24440      */
24441     this.useShim = false;
24442     
24443     /** @private */
24444     this.shim = null;
24445     
24446     if(!existingProxy){
24447         /** @private */
24448         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24449     }else{
24450         this.proxy = Roo.get(existingProxy).dom;
24451     }
24452     /** @private */
24453     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24454     
24455     /** @private */
24456     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24457     
24458     /** @private */
24459     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24460     
24461     /** @private */
24462     this.dragSpecs = {};
24463     
24464     /**
24465      * @private The adapter to use to positon and resize elements
24466      */
24467     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24468     this.adapter.init(this);
24469     
24470     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24471         /** @private */
24472         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24473         this.el.addClass("x-splitbar-h");
24474     }else{
24475         /** @private */
24476         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24477         this.el.addClass("x-splitbar-v");
24478     }
24479     
24480     this.addEvents({
24481         /**
24482          * @event resize
24483          * Fires when the splitter is moved (alias for {@link #event-moved})
24484          * @param {Roo.SplitBar} this
24485          * @param {Number} newSize the new width or height
24486          */
24487         "resize" : true,
24488         /**
24489          * @event moved
24490          * Fires when the splitter is moved
24491          * @param {Roo.SplitBar} this
24492          * @param {Number} newSize the new width or height
24493          */
24494         "moved" : true,
24495         /**
24496          * @event beforeresize
24497          * Fires before the splitter is dragged
24498          * @param {Roo.SplitBar} this
24499          */
24500         "beforeresize" : true,
24501
24502         "beforeapply" : true
24503     });
24504
24505     Roo.util.Observable.call(this);
24506 };
24507
24508 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24509     onStartProxyDrag : function(x, y){
24510         this.fireEvent("beforeresize", this);
24511         if(!this.overlay){
24512             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24513             o.unselectable();
24514             o.enableDisplayMode("block");
24515             // all splitbars share the same overlay
24516             Roo.SplitBar.prototype.overlay = o;
24517         }
24518         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24519         this.overlay.show();
24520         Roo.get(this.proxy).setDisplayed("block");
24521         var size = this.adapter.getElementSize(this);
24522         this.activeMinSize = this.getMinimumSize();;
24523         this.activeMaxSize = this.getMaximumSize();;
24524         var c1 = size - this.activeMinSize;
24525         var c2 = Math.max(this.activeMaxSize - size, 0);
24526         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24527             this.dd.resetConstraints();
24528             this.dd.setXConstraint(
24529                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24530                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24531             );
24532             this.dd.setYConstraint(0, 0);
24533         }else{
24534             this.dd.resetConstraints();
24535             this.dd.setXConstraint(0, 0);
24536             this.dd.setYConstraint(
24537                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24538                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24539             );
24540          }
24541         this.dragSpecs.startSize = size;
24542         this.dragSpecs.startPoint = [x, y];
24543         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24544     },
24545     
24546     /** 
24547      * @private Called after the drag operation by the DDProxy
24548      */
24549     onEndProxyDrag : function(e){
24550         Roo.get(this.proxy).setDisplayed(false);
24551         var endPoint = Roo.lib.Event.getXY(e);
24552         if(this.overlay){
24553             this.overlay.hide();
24554         }
24555         var newSize;
24556         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24557             newSize = this.dragSpecs.startSize + 
24558                 (this.placement == Roo.SplitBar.LEFT ?
24559                     endPoint[0] - this.dragSpecs.startPoint[0] :
24560                     this.dragSpecs.startPoint[0] - endPoint[0]
24561                 );
24562         }else{
24563             newSize = this.dragSpecs.startSize + 
24564                 (this.placement == Roo.SplitBar.TOP ?
24565                     endPoint[1] - this.dragSpecs.startPoint[1] :
24566                     this.dragSpecs.startPoint[1] - endPoint[1]
24567                 );
24568         }
24569         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24570         if(newSize != this.dragSpecs.startSize){
24571             if(this.fireEvent('beforeapply', this, newSize) !== false){
24572                 this.adapter.setElementSize(this, newSize);
24573                 this.fireEvent("moved", this, newSize);
24574                 this.fireEvent("resize", this, newSize);
24575             }
24576         }
24577     },
24578     
24579     /**
24580      * Get the adapter this SplitBar uses
24581      * @return The adapter object
24582      */
24583     getAdapter : function(){
24584         return this.adapter;
24585     },
24586     
24587     /**
24588      * Set the adapter this SplitBar uses
24589      * @param {Object} adapter A SplitBar adapter object
24590      */
24591     setAdapter : function(adapter){
24592         this.adapter = adapter;
24593         this.adapter.init(this);
24594     },
24595     
24596     /**
24597      * Gets the minimum size for the resizing element
24598      * @return {Number} The minimum size
24599      */
24600     getMinimumSize : function(){
24601         return this.minSize;
24602     },
24603     
24604     /**
24605      * Sets the minimum size for the resizing element
24606      * @param {Number} minSize The minimum size
24607      */
24608     setMinimumSize : function(minSize){
24609         this.minSize = minSize;
24610     },
24611     
24612     /**
24613      * Gets the maximum size for the resizing element
24614      * @return {Number} The maximum size
24615      */
24616     getMaximumSize : function(){
24617         return this.maxSize;
24618     },
24619     
24620     /**
24621      * Sets the maximum size for the resizing element
24622      * @param {Number} maxSize The maximum size
24623      */
24624     setMaximumSize : function(maxSize){
24625         this.maxSize = maxSize;
24626     },
24627     
24628     /**
24629      * Sets the initialize size for the resizing element
24630      * @param {Number} size The initial size
24631      */
24632     setCurrentSize : function(size){
24633         var oldAnimate = this.animate;
24634         this.animate = false;
24635         this.adapter.setElementSize(this, size);
24636         this.animate = oldAnimate;
24637     },
24638     
24639     /**
24640      * Destroy this splitbar. 
24641      * @param {Boolean} removeEl True to remove the element
24642      */
24643     destroy : function(removeEl){
24644         if(this.shim){
24645             this.shim.remove();
24646         }
24647         this.dd.unreg();
24648         this.proxy.parentNode.removeChild(this.proxy);
24649         if(removeEl){
24650             this.el.remove();
24651         }
24652     }
24653 });
24654
24655 /**
24656  * @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.
24657  */
24658 Roo.SplitBar.createProxy = function(dir){
24659     var proxy = new Roo.Element(document.createElement("div"));
24660     proxy.unselectable();
24661     var cls = 'x-splitbar-proxy';
24662     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24663     document.body.appendChild(proxy.dom);
24664     return proxy.dom;
24665 };
24666
24667 /** 
24668  * @class Roo.SplitBar.BasicLayoutAdapter
24669  * Default Adapter. It assumes the splitter and resizing element are not positioned
24670  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24671  */
24672 Roo.SplitBar.BasicLayoutAdapter = function(){
24673 };
24674
24675 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24676     // do nothing for now
24677     init : function(s){
24678     
24679     },
24680     /**
24681      * Called before drag operations to get the current size of the resizing element. 
24682      * @param {Roo.SplitBar} s The SplitBar using this adapter
24683      */
24684      getElementSize : function(s){
24685         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24686             return s.resizingEl.getWidth();
24687         }else{
24688             return s.resizingEl.getHeight();
24689         }
24690     },
24691     
24692     /**
24693      * Called after drag operations to set the size of the resizing element.
24694      * @param {Roo.SplitBar} s The SplitBar using this adapter
24695      * @param {Number} newSize The new size to set
24696      * @param {Function} onComplete A function to be invoked when resizing is complete
24697      */
24698     setElementSize : function(s, newSize, onComplete){
24699         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24700             if(!s.animate){
24701                 s.resizingEl.setWidth(newSize);
24702                 if(onComplete){
24703                     onComplete(s, newSize);
24704                 }
24705             }else{
24706                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24707             }
24708         }else{
24709             
24710             if(!s.animate){
24711                 s.resizingEl.setHeight(newSize);
24712                 if(onComplete){
24713                     onComplete(s, newSize);
24714                 }
24715             }else{
24716                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24717             }
24718         }
24719     }
24720 };
24721
24722 /** 
24723  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24724  * @extends Roo.SplitBar.BasicLayoutAdapter
24725  * Adapter that  moves the splitter element to align with the resized sizing element. 
24726  * Used with an absolute positioned SplitBar.
24727  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24728  * document.body, make sure you assign an id to the body element.
24729  */
24730 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24731     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24732     this.container = Roo.get(container);
24733 };
24734
24735 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24736     init : function(s){
24737         this.basic.init(s);
24738     },
24739     
24740     getElementSize : function(s){
24741         return this.basic.getElementSize(s);
24742     },
24743     
24744     setElementSize : function(s, newSize, onComplete){
24745         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24746     },
24747     
24748     moveSplitter : function(s){
24749         var yes = Roo.SplitBar;
24750         switch(s.placement){
24751             case yes.LEFT:
24752                 s.el.setX(s.resizingEl.getRight());
24753                 break;
24754             case yes.RIGHT:
24755                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24756                 break;
24757             case yes.TOP:
24758                 s.el.setY(s.resizingEl.getBottom());
24759                 break;
24760             case yes.BOTTOM:
24761                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24762                 break;
24763         }
24764     }
24765 };
24766
24767 /**
24768  * Orientation constant - Create a vertical SplitBar
24769  * @static
24770  * @type Number
24771  */
24772 Roo.SplitBar.VERTICAL = 1;
24773
24774 /**
24775  * Orientation constant - Create a horizontal SplitBar
24776  * @static
24777  * @type Number
24778  */
24779 Roo.SplitBar.HORIZONTAL = 2;
24780
24781 /**
24782  * Placement constant - The resizing element is to the left of the splitter element
24783  * @static
24784  * @type Number
24785  */
24786 Roo.SplitBar.LEFT = 1;
24787
24788 /**
24789  * Placement constant - The resizing element is to the right of the splitter element
24790  * @static
24791  * @type Number
24792  */
24793 Roo.SplitBar.RIGHT = 2;
24794
24795 /**
24796  * Placement constant - The resizing element is positioned above the splitter element
24797  * @static
24798  * @type Number
24799  */
24800 Roo.SplitBar.TOP = 3;
24801
24802 /**
24803  * Placement constant - The resizing element is positioned under splitter element
24804  * @static
24805  * @type Number
24806  */
24807 Roo.SplitBar.BOTTOM = 4;
24808 /*
24809  * Based on:
24810  * Ext JS Library 1.1.1
24811  * Copyright(c) 2006-2007, Ext JS, LLC.
24812  *
24813  * Originally Released Under LGPL - original licence link has changed is not relivant.
24814  *
24815  * Fork - LGPL
24816  * <script type="text/javascript">
24817  */
24818
24819 /**
24820  * @class Roo.View
24821  * @extends Roo.util.Observable
24822  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24823  * This class also supports single and multi selection modes. <br>
24824  * Create a data model bound view:
24825  <pre><code>
24826  var store = new Roo.data.Store(...);
24827
24828  var view = new Roo.View({
24829     el : "my-element",
24830     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24831  
24832     singleSelect: true,
24833     selectedClass: "ydataview-selected",
24834     store: store
24835  });
24836
24837  // listen for node click?
24838  view.on("click", function(vw, index, node, e){
24839  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24840  });
24841
24842  // load XML data
24843  dataModel.load("foobar.xml");
24844  </code></pre>
24845  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24846  * <br><br>
24847  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24848  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24849  * 
24850  * Note: old style constructor is still suported (container, template, config)
24851  * 
24852  * @constructor
24853  * Create a new View
24854  * @param {Object} config The config object
24855  * 
24856  */
24857 Roo.View = function(config, depreciated_tpl, depreciated_config){
24858     
24859     this.parent = false;
24860     
24861     if (typeof(depreciated_tpl) == 'undefined') {
24862         // new way.. - universal constructor.
24863         Roo.apply(this, config);
24864         this.el  = Roo.get(this.el);
24865     } else {
24866         // old format..
24867         this.el  = Roo.get(config);
24868         this.tpl = depreciated_tpl;
24869         Roo.apply(this, depreciated_config);
24870     }
24871     this.wrapEl  = this.el.wrap().wrap();
24872     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24873     
24874     
24875     if(typeof(this.tpl) == "string"){
24876         this.tpl = new Roo.Template(this.tpl);
24877     } else {
24878         // support xtype ctors..
24879         this.tpl = new Roo.factory(this.tpl, Roo);
24880     }
24881     
24882     
24883     this.tpl.compile();
24884     
24885     /** @private */
24886     this.addEvents({
24887         /**
24888          * @event beforeclick
24889          * Fires before a click is processed. Returns false to cancel the default action.
24890          * @param {Roo.View} this
24891          * @param {Number} index The index of the target node
24892          * @param {HTMLElement} node The target node
24893          * @param {Roo.EventObject} e The raw event object
24894          */
24895             "beforeclick" : true,
24896         /**
24897          * @event click
24898          * Fires when a template node is clicked.
24899          * @param {Roo.View} this
24900          * @param {Number} index The index of the target node
24901          * @param {HTMLElement} node The target node
24902          * @param {Roo.EventObject} e The raw event object
24903          */
24904             "click" : true,
24905         /**
24906          * @event dblclick
24907          * Fires when a template node is double clicked.
24908          * @param {Roo.View} this
24909          * @param {Number} index The index of the target node
24910          * @param {HTMLElement} node The target node
24911          * @param {Roo.EventObject} e The raw event object
24912          */
24913             "dblclick" : true,
24914         /**
24915          * @event contextmenu
24916          * Fires when a template node is right clicked.
24917          * @param {Roo.View} this
24918          * @param {Number} index The index of the target node
24919          * @param {HTMLElement} node The target node
24920          * @param {Roo.EventObject} e The raw event object
24921          */
24922             "contextmenu" : true,
24923         /**
24924          * @event selectionchange
24925          * Fires when the selected nodes change.
24926          * @param {Roo.View} this
24927          * @param {Array} selections Array of the selected nodes
24928          */
24929             "selectionchange" : true,
24930     
24931         /**
24932          * @event beforeselect
24933          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24934          * @param {Roo.View} this
24935          * @param {HTMLElement} node The node to be selected
24936          * @param {Array} selections Array of currently selected nodes
24937          */
24938             "beforeselect" : true,
24939         /**
24940          * @event preparedata
24941          * Fires on every row to render, to allow you to change the data.
24942          * @param {Roo.View} this
24943          * @param {Object} data to be rendered (change this)
24944          */
24945           "preparedata" : true
24946           
24947           
24948         });
24949
24950
24951
24952     this.el.on({
24953         "click": this.onClick,
24954         "dblclick": this.onDblClick,
24955         "contextmenu": this.onContextMenu,
24956         scope:this
24957     });
24958
24959     this.selections = [];
24960     this.nodes = [];
24961     this.cmp = new Roo.CompositeElementLite([]);
24962     if(this.store){
24963         this.store = Roo.factory(this.store, Roo.data);
24964         this.setStore(this.store, true);
24965     }
24966     
24967     if ( this.footer && this.footer.xtype) {
24968            
24969          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24970         
24971         this.footer.dataSource = this.store
24972         this.footer.container = fctr;
24973         this.footer = Roo.factory(this.footer, Roo);
24974         fctr.insertFirst(this.el);
24975         
24976         // this is a bit insane - as the paging toolbar seems to detach the el..
24977 //        dom.parentNode.parentNode.parentNode
24978          // they get detached?
24979     }
24980     
24981     
24982     Roo.View.superclass.constructor.call(this);
24983     
24984     
24985 };
24986
24987 Roo.extend(Roo.View, Roo.util.Observable, {
24988     
24989      /**
24990      * @cfg {Roo.data.Store} store Data store to load data from.
24991      */
24992     store : false,
24993     
24994     /**
24995      * @cfg {String|Roo.Element} el The container element.
24996      */
24997     el : '',
24998     
24999     /**
25000      * @cfg {String|Roo.Template} tpl The template used by this View 
25001      */
25002     tpl : false,
25003     /**
25004      * @cfg {String} dataName the named area of the template to use as the data area
25005      *                          Works with domtemplates roo-name="name"
25006      */
25007     dataName: false,
25008     /**
25009      * @cfg {String} selectedClass The css class to add to selected nodes
25010      */
25011     selectedClass : "x-view-selected",
25012      /**
25013      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25014      */
25015     emptyText : "",
25016     
25017     /**
25018      * @cfg {String} text to display on mask (default Loading)
25019      */
25020     mask : false,
25021     /**
25022      * @cfg {Boolean} multiSelect Allow multiple selection
25023      */
25024     multiSelect : false,
25025     /**
25026      * @cfg {Boolean} singleSelect Allow single selection
25027      */
25028     singleSelect:  false,
25029     
25030     /**
25031      * @cfg {Boolean} toggleSelect - selecting 
25032      */
25033     toggleSelect : false,
25034     
25035     /**
25036      * @cfg {Boolean} tickable - selecting 
25037      */
25038     tickable : false,
25039     
25040     /**
25041      * Returns the element this view is bound to.
25042      * @return {Roo.Element}
25043      */
25044     getEl : function(){
25045         return this.wrapEl;
25046     },
25047     
25048     
25049
25050     /**
25051      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25052      */
25053     refresh : function(){
25054         Roo.log('refresh');
25055         var t = this.tpl;
25056         
25057         // if we are using something like 'domtemplate', then
25058         // the what gets used is:
25059         // t.applySubtemplate(NAME, data, wrapping data..)
25060         // the outer template then get' applied with
25061         //     the store 'extra data'
25062         // and the body get's added to the
25063         //      roo-name="data" node?
25064         //      <span class='roo-tpl-{name}'></span> ?????
25065         
25066         
25067         
25068         this.clearSelections();
25069         this.el.update("");
25070         var html = [];
25071         var records = this.store.getRange();
25072         if(records.length < 1) {
25073             
25074             // is this valid??  = should it render a template??
25075             
25076             this.el.update(this.emptyText);
25077             return;
25078         }
25079         var el = this.el;
25080         if (this.dataName) {
25081             this.el.update(t.apply(this.store.meta)); //????
25082             el = this.el.child('.roo-tpl-' + this.dataName);
25083         }
25084         
25085         for(var i = 0, len = records.length; i < len; i++){
25086             var data = this.prepareData(records[i].data, i, records[i]);
25087             this.fireEvent("preparedata", this, data, i, records[i]);
25088             
25089             var d = Roo.apply({}, data);
25090             
25091             if(this.tickable){
25092                 Roo.apply(d, {'roo-id' : Roo.id()});
25093                 
25094                 var _this = this;
25095             
25096                 Roo.each(this.parent.item, function(item){
25097                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25098                         return;
25099                     }
25100                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25101                 });
25102             }
25103             
25104             html[html.length] = Roo.util.Format.trim(
25105                 this.dataName ?
25106                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25107                     t.apply(d)
25108             );
25109         }
25110         
25111         
25112         
25113         el.update(html.join(""));
25114         this.nodes = el.dom.childNodes;
25115         this.updateIndexes(0);
25116     },
25117     
25118
25119     /**
25120      * Function to override to reformat the data that is sent to
25121      * the template for each node.
25122      * DEPRICATED - use the preparedata event handler.
25123      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25124      * a JSON object for an UpdateManager bound view).
25125      */
25126     prepareData : function(data, index, record)
25127     {
25128         this.fireEvent("preparedata", this, data, index, record);
25129         return data;
25130     },
25131
25132     onUpdate : function(ds, record){
25133          Roo.log('on update');   
25134         this.clearSelections();
25135         var index = this.store.indexOf(record);
25136         var n = this.nodes[index];
25137         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25138         n.parentNode.removeChild(n);
25139         this.updateIndexes(index, index);
25140     },
25141
25142     
25143     
25144 // --------- FIXME     
25145     onAdd : function(ds, records, index)
25146     {
25147         Roo.log(['on Add', ds, records, index] );        
25148         this.clearSelections();
25149         if(this.nodes.length == 0){
25150             this.refresh();
25151             return;
25152         }
25153         var n = this.nodes[index];
25154         for(var i = 0, len = records.length; i < len; i++){
25155             var d = this.prepareData(records[i].data, i, records[i]);
25156             if(n){
25157                 this.tpl.insertBefore(n, d);
25158             }else{
25159                 
25160                 this.tpl.append(this.el, d);
25161             }
25162         }
25163         this.updateIndexes(index);
25164     },
25165
25166     onRemove : function(ds, record, index){
25167         Roo.log('onRemove');
25168         this.clearSelections();
25169         var el = this.dataName  ?
25170             this.el.child('.roo-tpl-' + this.dataName) :
25171             this.el; 
25172         
25173         el.dom.removeChild(this.nodes[index]);
25174         this.updateIndexes(index);
25175     },
25176
25177     /**
25178      * Refresh an individual node.
25179      * @param {Number} index
25180      */
25181     refreshNode : function(index){
25182         this.onUpdate(this.store, this.store.getAt(index));
25183     },
25184
25185     updateIndexes : function(startIndex, endIndex){
25186         var ns = this.nodes;
25187         startIndex = startIndex || 0;
25188         endIndex = endIndex || ns.length - 1;
25189         for(var i = startIndex; i <= endIndex; i++){
25190             ns[i].nodeIndex = i;
25191         }
25192     },
25193
25194     /**
25195      * Changes the data store this view uses and refresh the view.
25196      * @param {Store} store
25197      */
25198     setStore : function(store, initial){
25199         if(!initial && this.store){
25200             this.store.un("datachanged", this.refresh);
25201             this.store.un("add", this.onAdd);
25202             this.store.un("remove", this.onRemove);
25203             this.store.un("update", this.onUpdate);
25204             this.store.un("clear", this.refresh);
25205             this.store.un("beforeload", this.onBeforeLoad);
25206             this.store.un("load", this.onLoad);
25207             this.store.un("loadexception", this.onLoad);
25208         }
25209         if(store){
25210           
25211             store.on("datachanged", this.refresh, this);
25212             store.on("add", this.onAdd, this);
25213             store.on("remove", this.onRemove, this);
25214             store.on("update", this.onUpdate, this);
25215             store.on("clear", this.refresh, this);
25216             store.on("beforeload", this.onBeforeLoad, this);
25217             store.on("load", this.onLoad, this);
25218             store.on("loadexception", this.onLoad, this);
25219         }
25220         
25221         if(store){
25222             this.refresh();
25223         }
25224     },
25225     /**
25226      * onbeforeLoad - masks the loading area.
25227      *
25228      */
25229     onBeforeLoad : function(store,opts)
25230     {
25231          Roo.log('onBeforeLoad');   
25232         if (!opts.add) {
25233             this.el.update("");
25234         }
25235         this.el.mask(this.mask ? this.mask : "Loading" ); 
25236     },
25237     onLoad : function ()
25238     {
25239         this.el.unmask();
25240     },
25241     
25242
25243     /**
25244      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25245      * @param {HTMLElement} node
25246      * @return {HTMLElement} The template node
25247      */
25248     findItemFromChild : function(node){
25249         var el = this.dataName  ?
25250             this.el.child('.roo-tpl-' + this.dataName,true) :
25251             this.el.dom; 
25252         
25253         if(!node || node.parentNode == el){
25254                     return node;
25255             }
25256             var p = node.parentNode;
25257             while(p && p != el){
25258             if(p.parentNode == el){
25259                 return p;
25260             }
25261             p = p.parentNode;
25262         }
25263             return null;
25264     },
25265
25266     /** @ignore */
25267     onClick : function(e){
25268         var item = this.findItemFromChild(e.getTarget());
25269         if(item){
25270             var index = this.indexOf(item);
25271             if(this.onItemClick(item, index, e) !== false){
25272                 this.fireEvent("click", this, index, item, e);
25273             }
25274         }else{
25275             this.clearSelections();
25276         }
25277     },
25278
25279     /** @ignore */
25280     onContextMenu : function(e){
25281         var item = this.findItemFromChild(e.getTarget());
25282         if(item){
25283             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25284         }
25285     },
25286
25287     /** @ignore */
25288     onDblClick : function(e){
25289         var item = this.findItemFromChild(e.getTarget());
25290         if(item){
25291             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25292         }
25293     },
25294
25295     onItemClick : function(item, index, e)
25296     {
25297         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25298             return false;
25299         }
25300         if (this.toggleSelect) {
25301             var m = this.isSelected(item) ? 'unselect' : 'select';
25302             Roo.log(m);
25303             var _t = this;
25304             _t[m](item, true, false);
25305             return true;
25306         }
25307         if(this.multiSelect || this.singleSelect){
25308             if(this.multiSelect && e.shiftKey && this.lastSelection){
25309                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25310             }else{
25311                 this.select(item, this.multiSelect && e.ctrlKey);
25312                 this.lastSelection = item;
25313             }
25314             
25315             if(!this.tickable){
25316                 e.preventDefault();
25317             }
25318             
25319         }
25320         return true;
25321     },
25322
25323     /**
25324      * Get the number of selected nodes.
25325      * @return {Number}
25326      */
25327     getSelectionCount : function(){
25328         return this.selections.length;
25329     },
25330
25331     /**
25332      * Get the currently selected nodes.
25333      * @return {Array} An array of HTMLElements
25334      */
25335     getSelectedNodes : function(){
25336         return this.selections;
25337     },
25338
25339     /**
25340      * Get the indexes of the selected nodes.
25341      * @return {Array}
25342      */
25343     getSelectedIndexes : function(){
25344         var indexes = [], s = this.selections;
25345         for(var i = 0, len = s.length; i < len; i++){
25346             indexes.push(s[i].nodeIndex);
25347         }
25348         return indexes;
25349     },
25350
25351     /**
25352      * Clear all selections
25353      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25354      */
25355     clearSelections : function(suppressEvent){
25356         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25357             this.cmp.elements = this.selections;
25358             this.cmp.removeClass(this.selectedClass);
25359             this.selections = [];
25360             if(!suppressEvent){
25361                 this.fireEvent("selectionchange", this, this.selections);
25362             }
25363         }
25364     },
25365
25366     /**
25367      * Returns true if the passed node is selected
25368      * @param {HTMLElement/Number} node The node or node index
25369      * @return {Boolean}
25370      */
25371     isSelected : function(node){
25372         var s = this.selections;
25373         if(s.length < 1){
25374             return false;
25375         }
25376         node = this.getNode(node);
25377         return s.indexOf(node) !== -1;
25378     },
25379
25380     /**
25381      * Selects nodes.
25382      * @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
25383      * @param {Boolean} keepExisting (optional) true to keep existing selections
25384      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25385      */
25386     select : function(nodeInfo, keepExisting, suppressEvent){
25387         if(nodeInfo instanceof Array){
25388             if(!keepExisting){
25389                 this.clearSelections(true);
25390             }
25391             for(var i = 0, len = nodeInfo.length; i < len; i++){
25392                 this.select(nodeInfo[i], true, true);
25393             }
25394             return;
25395         } 
25396         var node = this.getNode(nodeInfo);
25397         if(!node || this.isSelected(node)){
25398             return; // already selected.
25399         }
25400         if(!keepExisting){
25401             this.clearSelections(true);
25402         }
25403         
25404         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25405             Roo.fly(node).addClass(this.selectedClass);
25406             this.selections.push(node);
25407             if(!suppressEvent){
25408                 this.fireEvent("selectionchange", this, this.selections);
25409             }
25410         }
25411         
25412         
25413     },
25414       /**
25415      * Unselects nodes.
25416      * @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
25417      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25418      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25419      */
25420     unselect : function(nodeInfo, keepExisting, suppressEvent)
25421     {
25422         if(nodeInfo instanceof Array){
25423             Roo.each(this.selections, function(s) {
25424                 this.unselect(s, nodeInfo);
25425             }, this);
25426             return;
25427         }
25428         var node = this.getNode(nodeInfo);
25429         if(!node || !this.isSelected(node)){
25430             Roo.log("not selected");
25431             return; // not selected.
25432         }
25433         // fireevent???
25434         var ns = [];
25435         Roo.each(this.selections, function(s) {
25436             if (s == node ) {
25437                 Roo.fly(node).removeClass(this.selectedClass);
25438
25439                 return;
25440             }
25441             ns.push(s);
25442         },this);
25443         
25444         this.selections= ns;
25445         this.fireEvent("selectionchange", this, this.selections);
25446     },
25447
25448     /**
25449      * Gets a template node.
25450      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25451      * @return {HTMLElement} The node or null if it wasn't found
25452      */
25453     getNode : function(nodeInfo){
25454         if(typeof nodeInfo == "string"){
25455             return document.getElementById(nodeInfo);
25456         }else if(typeof nodeInfo == "number"){
25457             return this.nodes[nodeInfo];
25458         }
25459         return nodeInfo;
25460     },
25461
25462     /**
25463      * Gets a range template nodes.
25464      * @param {Number} startIndex
25465      * @param {Number} endIndex
25466      * @return {Array} An array of nodes
25467      */
25468     getNodes : function(start, end){
25469         var ns = this.nodes;
25470         start = start || 0;
25471         end = typeof end == "undefined" ? ns.length - 1 : end;
25472         var nodes = [];
25473         if(start <= end){
25474             for(var i = start; i <= end; i++){
25475                 nodes.push(ns[i]);
25476             }
25477         } else{
25478             for(var i = start; i >= end; i--){
25479                 nodes.push(ns[i]);
25480             }
25481         }
25482         return nodes;
25483     },
25484
25485     /**
25486      * Finds the index of the passed node
25487      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25488      * @return {Number} The index of the node or -1
25489      */
25490     indexOf : function(node){
25491         node = this.getNode(node);
25492         if(typeof node.nodeIndex == "number"){
25493             return node.nodeIndex;
25494         }
25495         var ns = this.nodes;
25496         for(var i = 0, len = ns.length; i < len; i++){
25497             if(ns[i] == node){
25498                 return i;
25499             }
25500         }
25501         return -1;
25502     }
25503 });
25504 /*
25505  * Based on:
25506  * Ext JS Library 1.1.1
25507  * Copyright(c) 2006-2007, Ext JS, LLC.
25508  *
25509  * Originally Released Under LGPL - original licence link has changed is not relivant.
25510  *
25511  * Fork - LGPL
25512  * <script type="text/javascript">
25513  */
25514
25515 /**
25516  * @class Roo.JsonView
25517  * @extends Roo.View
25518  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25519 <pre><code>
25520 var view = new Roo.JsonView({
25521     container: "my-element",
25522     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25523     multiSelect: true, 
25524     jsonRoot: "data" 
25525 });
25526
25527 // listen for node click?
25528 view.on("click", function(vw, index, node, e){
25529     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25530 });
25531
25532 // direct load of JSON data
25533 view.load("foobar.php");
25534
25535 // Example from my blog list
25536 var tpl = new Roo.Template(
25537     '&lt;div class="entry"&gt;' +
25538     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25539     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25540     "&lt;/div&gt;&lt;hr /&gt;"
25541 );
25542
25543 var moreView = new Roo.JsonView({
25544     container :  "entry-list", 
25545     template : tpl,
25546     jsonRoot: "posts"
25547 });
25548 moreView.on("beforerender", this.sortEntries, this);
25549 moreView.load({
25550     url: "/blog/get-posts.php",
25551     params: "allposts=true",
25552     text: "Loading Blog Entries..."
25553 });
25554 </code></pre>
25555
25556 * Note: old code is supported with arguments : (container, template, config)
25557
25558
25559  * @constructor
25560  * Create a new JsonView
25561  * 
25562  * @param {Object} config The config object
25563  * 
25564  */
25565 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25566     
25567     
25568     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25569
25570     var um = this.el.getUpdateManager();
25571     um.setRenderer(this);
25572     um.on("update", this.onLoad, this);
25573     um.on("failure", this.onLoadException, this);
25574
25575     /**
25576      * @event beforerender
25577      * Fires before rendering of the downloaded JSON data.
25578      * @param {Roo.JsonView} this
25579      * @param {Object} data The JSON data loaded
25580      */
25581     /**
25582      * @event load
25583      * Fires when data is loaded.
25584      * @param {Roo.JsonView} this
25585      * @param {Object} data The JSON data loaded
25586      * @param {Object} response The raw Connect response object
25587      */
25588     /**
25589      * @event loadexception
25590      * Fires when loading fails.
25591      * @param {Roo.JsonView} this
25592      * @param {Object} response The raw Connect response object
25593      */
25594     this.addEvents({
25595         'beforerender' : true,
25596         'load' : true,
25597         'loadexception' : true
25598     });
25599 };
25600 Roo.extend(Roo.JsonView, Roo.View, {
25601     /**
25602      * @type {String} The root property in the loaded JSON object that contains the data
25603      */
25604     jsonRoot : "",
25605
25606     /**
25607      * Refreshes the view.
25608      */
25609     refresh : function(){
25610         this.clearSelections();
25611         this.el.update("");
25612         var html = [];
25613         var o = this.jsonData;
25614         if(o && o.length > 0){
25615             for(var i = 0, len = o.length; i < len; i++){
25616                 var data = this.prepareData(o[i], i, o);
25617                 html[html.length] = this.tpl.apply(data);
25618             }
25619         }else{
25620             html.push(this.emptyText);
25621         }
25622         this.el.update(html.join(""));
25623         this.nodes = this.el.dom.childNodes;
25624         this.updateIndexes(0);
25625     },
25626
25627     /**
25628      * 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.
25629      * @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:
25630      <pre><code>
25631      view.load({
25632          url: "your-url.php",
25633          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25634          callback: yourFunction,
25635          scope: yourObject, //(optional scope)
25636          discardUrl: false,
25637          nocache: false,
25638          text: "Loading...",
25639          timeout: 30,
25640          scripts: false
25641      });
25642      </code></pre>
25643      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25644      * 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.
25645      * @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}
25646      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25647      * @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.
25648      */
25649     load : function(){
25650         var um = this.el.getUpdateManager();
25651         um.update.apply(um, arguments);
25652     },
25653
25654     render : function(el, response){
25655         this.clearSelections();
25656         this.el.update("");
25657         var o;
25658         try{
25659             o = Roo.util.JSON.decode(response.responseText);
25660             if(this.jsonRoot){
25661                 
25662                 o = o[this.jsonRoot];
25663             }
25664         } catch(e){
25665         }
25666         /**
25667          * The current JSON data or null
25668          */
25669         this.jsonData = o;
25670         this.beforeRender();
25671         this.refresh();
25672     },
25673
25674 /**
25675  * Get the number of records in the current JSON dataset
25676  * @return {Number}
25677  */
25678     getCount : function(){
25679         return this.jsonData ? this.jsonData.length : 0;
25680     },
25681
25682 /**
25683  * Returns the JSON object for the specified node(s)
25684  * @param {HTMLElement/Array} node The node or an array of nodes
25685  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25686  * you get the JSON object for the node
25687  */
25688     getNodeData : function(node){
25689         if(node instanceof Array){
25690             var data = [];
25691             for(var i = 0, len = node.length; i < len; i++){
25692                 data.push(this.getNodeData(node[i]));
25693             }
25694             return data;
25695         }
25696         return this.jsonData[this.indexOf(node)] || null;
25697     },
25698
25699     beforeRender : function(){
25700         this.snapshot = this.jsonData;
25701         if(this.sortInfo){
25702             this.sort.apply(this, this.sortInfo);
25703         }
25704         this.fireEvent("beforerender", this, this.jsonData);
25705     },
25706
25707     onLoad : function(el, o){
25708         this.fireEvent("load", this, this.jsonData, o);
25709     },
25710
25711     onLoadException : function(el, o){
25712         this.fireEvent("loadexception", this, o);
25713     },
25714
25715 /**
25716  * Filter the data by a specific property.
25717  * @param {String} property A property on your JSON objects
25718  * @param {String/RegExp} value Either string that the property values
25719  * should start with, or a RegExp to test against the property
25720  */
25721     filter : function(property, value){
25722         if(this.jsonData){
25723             var data = [];
25724             var ss = this.snapshot;
25725             if(typeof value == "string"){
25726                 var vlen = value.length;
25727                 if(vlen == 0){
25728                     this.clearFilter();
25729                     return;
25730                 }
25731                 value = value.toLowerCase();
25732                 for(var i = 0, len = ss.length; i < len; i++){
25733                     var o = ss[i];
25734                     if(o[property].substr(0, vlen).toLowerCase() == value){
25735                         data.push(o);
25736                     }
25737                 }
25738             } else if(value.exec){ // regex?
25739                 for(var i = 0, len = ss.length; i < len; i++){
25740                     var o = ss[i];
25741                     if(value.test(o[property])){
25742                         data.push(o);
25743                     }
25744                 }
25745             } else{
25746                 return;
25747             }
25748             this.jsonData = data;
25749             this.refresh();
25750         }
25751     },
25752
25753 /**
25754  * Filter by a function. The passed function will be called with each
25755  * object in the current dataset. If the function returns true the value is kept,
25756  * otherwise it is filtered.
25757  * @param {Function} fn
25758  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25759  */
25760     filterBy : function(fn, scope){
25761         if(this.jsonData){
25762             var data = [];
25763             var ss = this.snapshot;
25764             for(var i = 0, len = ss.length; i < len; i++){
25765                 var o = ss[i];
25766                 if(fn.call(scope || this, o)){
25767                     data.push(o);
25768                 }
25769             }
25770             this.jsonData = data;
25771             this.refresh();
25772         }
25773     },
25774
25775 /**
25776  * Clears the current filter.
25777  */
25778     clearFilter : function(){
25779         if(this.snapshot && this.jsonData != this.snapshot){
25780             this.jsonData = this.snapshot;
25781             this.refresh();
25782         }
25783     },
25784
25785
25786 /**
25787  * Sorts the data for this view and refreshes it.
25788  * @param {String} property A property on your JSON objects to sort on
25789  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25790  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25791  */
25792     sort : function(property, dir, sortType){
25793         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25794         if(this.jsonData){
25795             var p = property;
25796             var dsc = dir && dir.toLowerCase() == "desc";
25797             var f = function(o1, o2){
25798                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25799                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25800                 ;
25801                 if(v1 < v2){
25802                     return dsc ? +1 : -1;
25803                 } else if(v1 > v2){
25804                     return dsc ? -1 : +1;
25805                 } else{
25806                     return 0;
25807                 }
25808             };
25809             this.jsonData.sort(f);
25810             this.refresh();
25811             if(this.jsonData != this.snapshot){
25812                 this.snapshot.sort(f);
25813             }
25814         }
25815     }
25816 });/*
25817  * Based on:
25818  * Ext JS Library 1.1.1
25819  * Copyright(c) 2006-2007, Ext JS, LLC.
25820  *
25821  * Originally Released Under LGPL - original licence link has changed is not relivant.
25822  *
25823  * Fork - LGPL
25824  * <script type="text/javascript">
25825  */
25826  
25827
25828 /**
25829  * @class Roo.ColorPalette
25830  * @extends Roo.Component
25831  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25832  * Here's an example of typical usage:
25833  * <pre><code>
25834 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25835 cp.render('my-div');
25836
25837 cp.on('select', function(palette, selColor){
25838     // do something with selColor
25839 });
25840 </code></pre>
25841  * @constructor
25842  * Create a new ColorPalette
25843  * @param {Object} config The config object
25844  */
25845 Roo.ColorPalette = function(config){
25846     Roo.ColorPalette.superclass.constructor.call(this, config);
25847     this.addEvents({
25848         /**
25849              * @event select
25850              * Fires when a color is selected
25851              * @param {ColorPalette} this
25852              * @param {String} color The 6-digit color hex code (without the # symbol)
25853              */
25854         select: true
25855     });
25856
25857     if(this.handler){
25858         this.on("select", this.handler, this.scope, true);
25859     }
25860 };
25861 Roo.extend(Roo.ColorPalette, Roo.Component, {
25862     /**
25863      * @cfg {String} itemCls
25864      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25865      */
25866     itemCls : "x-color-palette",
25867     /**
25868      * @cfg {String} value
25869      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25870      * the hex codes are case-sensitive.
25871      */
25872     value : null,
25873     clickEvent:'click',
25874     // private
25875     ctype: "Roo.ColorPalette",
25876
25877     /**
25878      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25879      */
25880     allowReselect : false,
25881
25882     /**
25883      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25884      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25885      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25886      * of colors with the width setting until the box is symmetrical.</p>
25887      * <p>You can override individual colors if needed:</p>
25888      * <pre><code>
25889 var cp = new Roo.ColorPalette();
25890 cp.colors[0] = "FF0000";  // change the first box to red
25891 </code></pre>
25892
25893 Or you can provide a custom array of your own for complete control:
25894 <pre><code>
25895 var cp = new Roo.ColorPalette();
25896 cp.colors = ["000000", "993300", "333300"];
25897 </code></pre>
25898      * @type Array
25899      */
25900     colors : [
25901         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25902         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25903         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25904         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25905         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25906     ],
25907
25908     // private
25909     onRender : function(container, position){
25910         var t = new Roo.MasterTemplate(
25911             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25912         );
25913         var c = this.colors;
25914         for(var i = 0, len = c.length; i < len; i++){
25915             t.add([c[i]]);
25916         }
25917         var el = document.createElement("div");
25918         el.className = this.itemCls;
25919         t.overwrite(el);
25920         container.dom.insertBefore(el, position);
25921         this.el = Roo.get(el);
25922         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25923         if(this.clickEvent != 'click'){
25924             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25925         }
25926     },
25927
25928     // private
25929     afterRender : function(){
25930         Roo.ColorPalette.superclass.afterRender.call(this);
25931         if(this.value){
25932             var s = this.value;
25933             this.value = null;
25934             this.select(s);
25935         }
25936     },
25937
25938     // private
25939     handleClick : function(e, t){
25940         e.preventDefault();
25941         if(!this.disabled){
25942             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25943             this.select(c.toUpperCase());
25944         }
25945     },
25946
25947     /**
25948      * Selects the specified color in the palette (fires the select event)
25949      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25950      */
25951     select : function(color){
25952         color = color.replace("#", "");
25953         if(color != this.value || this.allowReselect){
25954             var el = this.el;
25955             if(this.value){
25956                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25957             }
25958             el.child("a.color-"+color).addClass("x-color-palette-sel");
25959             this.value = color;
25960             this.fireEvent("select", this, color);
25961         }
25962     }
25963 });/*
25964  * Based on:
25965  * Ext JS Library 1.1.1
25966  * Copyright(c) 2006-2007, Ext JS, LLC.
25967  *
25968  * Originally Released Under LGPL - original licence link has changed is not relivant.
25969  *
25970  * Fork - LGPL
25971  * <script type="text/javascript">
25972  */
25973  
25974 /**
25975  * @class Roo.DatePicker
25976  * @extends Roo.Component
25977  * Simple date picker class.
25978  * @constructor
25979  * Create a new DatePicker
25980  * @param {Object} config The config object
25981  */
25982 Roo.DatePicker = function(config){
25983     Roo.DatePicker.superclass.constructor.call(this, config);
25984
25985     this.value = config && config.value ?
25986                  config.value.clearTime() : new Date().clearTime();
25987
25988     this.addEvents({
25989         /**
25990              * @event select
25991              * Fires when a date is selected
25992              * @param {DatePicker} this
25993              * @param {Date} date The selected date
25994              */
25995         'select': true,
25996         /**
25997              * @event monthchange
25998              * Fires when the displayed month changes 
25999              * @param {DatePicker} this
26000              * @param {Date} date The selected month
26001              */
26002         'monthchange': true
26003     });
26004
26005     if(this.handler){
26006         this.on("select", this.handler,  this.scope || this);
26007     }
26008     // build the disabledDatesRE
26009     if(!this.disabledDatesRE && this.disabledDates){
26010         var dd = this.disabledDates;
26011         var re = "(?:";
26012         for(var i = 0; i < dd.length; i++){
26013             re += dd[i];
26014             if(i != dd.length-1) re += "|";
26015         }
26016         this.disabledDatesRE = new RegExp(re + ")");
26017     }
26018 };
26019
26020 Roo.extend(Roo.DatePicker, Roo.Component, {
26021     /**
26022      * @cfg {String} todayText
26023      * The text to display on the button that selects the current date (defaults to "Today")
26024      */
26025     todayText : "Today",
26026     /**
26027      * @cfg {String} okText
26028      * The text to display on the ok button
26029      */
26030     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26031     /**
26032      * @cfg {String} cancelText
26033      * The text to display on the cancel button
26034      */
26035     cancelText : "Cancel",
26036     /**
26037      * @cfg {String} todayTip
26038      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26039      */
26040     todayTip : "{0} (Spacebar)",
26041     /**
26042      * @cfg {Date} minDate
26043      * Minimum allowable date (JavaScript date object, defaults to null)
26044      */
26045     minDate : null,
26046     /**
26047      * @cfg {Date} maxDate
26048      * Maximum allowable date (JavaScript date object, defaults to null)
26049      */
26050     maxDate : null,
26051     /**
26052      * @cfg {String} minText
26053      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26054      */
26055     minText : "This date is before the minimum date",
26056     /**
26057      * @cfg {String} maxText
26058      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26059      */
26060     maxText : "This date is after the maximum date",
26061     /**
26062      * @cfg {String} format
26063      * The default date format string which can be overriden for localization support.  The format must be
26064      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26065      */
26066     format : "m/d/y",
26067     /**
26068      * @cfg {Array} disabledDays
26069      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26070      */
26071     disabledDays : null,
26072     /**
26073      * @cfg {String} disabledDaysText
26074      * The tooltip to display when the date falls on a disabled day (defaults to "")
26075      */
26076     disabledDaysText : "",
26077     /**
26078      * @cfg {RegExp} disabledDatesRE
26079      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26080      */
26081     disabledDatesRE : null,
26082     /**
26083      * @cfg {String} disabledDatesText
26084      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26085      */
26086     disabledDatesText : "",
26087     /**
26088      * @cfg {Boolean} constrainToViewport
26089      * True to constrain the date picker to the viewport (defaults to true)
26090      */
26091     constrainToViewport : true,
26092     /**
26093      * @cfg {Array} monthNames
26094      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26095      */
26096     monthNames : Date.monthNames,
26097     /**
26098      * @cfg {Array} dayNames
26099      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26100      */
26101     dayNames : Date.dayNames,
26102     /**
26103      * @cfg {String} nextText
26104      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26105      */
26106     nextText: 'Next Month (Control+Right)',
26107     /**
26108      * @cfg {String} prevText
26109      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26110      */
26111     prevText: 'Previous Month (Control+Left)',
26112     /**
26113      * @cfg {String} monthYearText
26114      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26115      */
26116     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26117     /**
26118      * @cfg {Number} startDay
26119      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26120      */
26121     startDay : 0,
26122     /**
26123      * @cfg {Bool} showClear
26124      * Show a clear button (usefull for date form elements that can be blank.)
26125      */
26126     
26127     showClear: false,
26128     
26129     /**
26130      * Sets the value of the date field
26131      * @param {Date} value The date to set
26132      */
26133     setValue : function(value){
26134         var old = this.value;
26135         
26136         if (typeof(value) == 'string') {
26137          
26138             value = Date.parseDate(value, this.format);
26139         }
26140         if (!value) {
26141             value = new Date();
26142         }
26143         
26144         this.value = value.clearTime(true);
26145         if(this.el){
26146             this.update(this.value);
26147         }
26148     },
26149
26150     /**
26151      * Gets the current selected value of the date field
26152      * @return {Date} The selected date
26153      */
26154     getValue : function(){
26155         return this.value;
26156     },
26157
26158     // private
26159     focus : function(){
26160         if(this.el){
26161             this.update(this.activeDate);
26162         }
26163     },
26164
26165     // privateval
26166     onRender : function(container, position){
26167         
26168         var m = [
26169              '<table cellspacing="0">',
26170                 '<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>',
26171                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26172         var dn = this.dayNames;
26173         for(var i = 0; i < 7; i++){
26174             var d = this.startDay+i;
26175             if(d > 6){
26176                 d = d-7;
26177             }
26178             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26179         }
26180         m[m.length] = "</tr></thead><tbody><tr>";
26181         for(var i = 0; i < 42; i++) {
26182             if(i % 7 == 0 && i != 0){
26183                 m[m.length] = "</tr><tr>";
26184             }
26185             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26186         }
26187         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26188             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26189
26190         var el = document.createElement("div");
26191         el.className = "x-date-picker";
26192         el.innerHTML = m.join("");
26193
26194         container.dom.insertBefore(el, position);
26195
26196         this.el = Roo.get(el);
26197         this.eventEl = Roo.get(el.firstChild);
26198
26199         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26200             handler: this.showPrevMonth,
26201             scope: this,
26202             preventDefault:true,
26203             stopDefault:true
26204         });
26205
26206         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26207             handler: this.showNextMonth,
26208             scope: this,
26209             preventDefault:true,
26210             stopDefault:true
26211         });
26212
26213         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26214
26215         this.monthPicker = this.el.down('div.x-date-mp');
26216         this.monthPicker.enableDisplayMode('block');
26217         
26218         var kn = new Roo.KeyNav(this.eventEl, {
26219             "left" : function(e){
26220                 e.ctrlKey ?
26221                     this.showPrevMonth() :
26222                     this.update(this.activeDate.add("d", -1));
26223             },
26224
26225             "right" : function(e){
26226                 e.ctrlKey ?
26227                     this.showNextMonth() :
26228                     this.update(this.activeDate.add("d", 1));
26229             },
26230
26231             "up" : function(e){
26232                 e.ctrlKey ?
26233                     this.showNextYear() :
26234                     this.update(this.activeDate.add("d", -7));
26235             },
26236
26237             "down" : function(e){
26238                 e.ctrlKey ?
26239                     this.showPrevYear() :
26240                     this.update(this.activeDate.add("d", 7));
26241             },
26242
26243             "pageUp" : function(e){
26244                 this.showNextMonth();
26245             },
26246
26247             "pageDown" : function(e){
26248                 this.showPrevMonth();
26249             },
26250
26251             "enter" : function(e){
26252                 e.stopPropagation();
26253                 return true;
26254             },
26255
26256             scope : this
26257         });
26258
26259         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26260
26261         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26262
26263         this.el.unselectable();
26264         
26265         this.cells = this.el.select("table.x-date-inner tbody td");
26266         this.textNodes = this.el.query("table.x-date-inner tbody span");
26267
26268         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26269             text: "&#160;",
26270             tooltip: this.monthYearText
26271         });
26272
26273         this.mbtn.on('click', this.showMonthPicker, this);
26274         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26275
26276
26277         var today = (new Date()).dateFormat(this.format);
26278         
26279         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26280         if (this.showClear) {
26281             baseTb.add( new Roo.Toolbar.Fill());
26282         }
26283         baseTb.add({
26284             text: String.format(this.todayText, today),
26285             tooltip: String.format(this.todayTip, today),
26286             handler: this.selectToday,
26287             scope: this
26288         });
26289         
26290         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26291             
26292         //});
26293         if (this.showClear) {
26294             
26295             baseTb.add( new Roo.Toolbar.Fill());
26296             baseTb.add({
26297                 text: '&#160;',
26298                 cls: 'x-btn-icon x-btn-clear',
26299                 handler: function() {
26300                     //this.value = '';
26301                     this.fireEvent("select", this, '');
26302                 },
26303                 scope: this
26304             });
26305         }
26306         
26307         
26308         if(Roo.isIE){
26309             this.el.repaint();
26310         }
26311         this.update(this.value);
26312     },
26313
26314     createMonthPicker : function(){
26315         if(!this.monthPicker.dom.firstChild){
26316             var buf = ['<table border="0" cellspacing="0">'];
26317             for(var i = 0; i < 6; i++){
26318                 buf.push(
26319                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26320                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26321                     i == 0 ?
26322                     '<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>' :
26323                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26324                 );
26325             }
26326             buf.push(
26327                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26328                     this.okText,
26329                     '</button><button type="button" class="x-date-mp-cancel">',
26330                     this.cancelText,
26331                     '</button></td></tr>',
26332                 '</table>'
26333             );
26334             this.monthPicker.update(buf.join(''));
26335             this.monthPicker.on('click', this.onMonthClick, this);
26336             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26337
26338             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26339             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26340
26341             this.mpMonths.each(function(m, a, i){
26342                 i += 1;
26343                 if((i%2) == 0){
26344                     m.dom.xmonth = 5 + Math.round(i * .5);
26345                 }else{
26346                     m.dom.xmonth = Math.round((i-1) * .5);
26347                 }
26348             });
26349         }
26350     },
26351
26352     showMonthPicker : function(){
26353         this.createMonthPicker();
26354         var size = this.el.getSize();
26355         this.monthPicker.setSize(size);
26356         this.monthPicker.child('table').setSize(size);
26357
26358         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26359         this.updateMPMonth(this.mpSelMonth);
26360         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26361         this.updateMPYear(this.mpSelYear);
26362
26363         this.monthPicker.slideIn('t', {duration:.2});
26364     },
26365
26366     updateMPYear : function(y){
26367         this.mpyear = y;
26368         var ys = this.mpYears.elements;
26369         for(var i = 1; i <= 10; i++){
26370             var td = ys[i-1], y2;
26371             if((i%2) == 0){
26372                 y2 = y + Math.round(i * .5);
26373                 td.firstChild.innerHTML = y2;
26374                 td.xyear = y2;
26375             }else{
26376                 y2 = y - (5-Math.round(i * .5));
26377                 td.firstChild.innerHTML = y2;
26378                 td.xyear = y2;
26379             }
26380             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26381         }
26382     },
26383
26384     updateMPMonth : function(sm){
26385         this.mpMonths.each(function(m, a, i){
26386             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26387         });
26388     },
26389
26390     selectMPMonth: function(m){
26391         
26392     },
26393
26394     onMonthClick : function(e, t){
26395         e.stopEvent();
26396         var el = new Roo.Element(t), pn;
26397         if(el.is('button.x-date-mp-cancel')){
26398             this.hideMonthPicker();
26399         }
26400         else if(el.is('button.x-date-mp-ok')){
26401             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26402             this.hideMonthPicker();
26403         }
26404         else if(pn = el.up('td.x-date-mp-month', 2)){
26405             this.mpMonths.removeClass('x-date-mp-sel');
26406             pn.addClass('x-date-mp-sel');
26407             this.mpSelMonth = pn.dom.xmonth;
26408         }
26409         else if(pn = el.up('td.x-date-mp-year', 2)){
26410             this.mpYears.removeClass('x-date-mp-sel');
26411             pn.addClass('x-date-mp-sel');
26412             this.mpSelYear = pn.dom.xyear;
26413         }
26414         else if(el.is('a.x-date-mp-prev')){
26415             this.updateMPYear(this.mpyear-10);
26416         }
26417         else if(el.is('a.x-date-mp-next')){
26418             this.updateMPYear(this.mpyear+10);
26419         }
26420     },
26421
26422     onMonthDblClick : function(e, t){
26423         e.stopEvent();
26424         var el = new Roo.Element(t), pn;
26425         if(pn = el.up('td.x-date-mp-month', 2)){
26426             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26427             this.hideMonthPicker();
26428         }
26429         else if(pn = el.up('td.x-date-mp-year', 2)){
26430             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26431             this.hideMonthPicker();
26432         }
26433     },
26434
26435     hideMonthPicker : function(disableAnim){
26436         if(this.monthPicker){
26437             if(disableAnim === true){
26438                 this.monthPicker.hide();
26439             }else{
26440                 this.monthPicker.slideOut('t', {duration:.2});
26441             }
26442         }
26443     },
26444
26445     // private
26446     showPrevMonth : function(e){
26447         this.update(this.activeDate.add("mo", -1));
26448     },
26449
26450     // private
26451     showNextMonth : function(e){
26452         this.update(this.activeDate.add("mo", 1));
26453     },
26454
26455     // private
26456     showPrevYear : function(){
26457         this.update(this.activeDate.add("y", -1));
26458     },
26459
26460     // private
26461     showNextYear : function(){
26462         this.update(this.activeDate.add("y", 1));
26463     },
26464
26465     // private
26466     handleMouseWheel : function(e){
26467         var delta = e.getWheelDelta();
26468         if(delta > 0){
26469             this.showPrevMonth();
26470             e.stopEvent();
26471         } else if(delta < 0){
26472             this.showNextMonth();
26473             e.stopEvent();
26474         }
26475     },
26476
26477     // private
26478     handleDateClick : function(e, t){
26479         e.stopEvent();
26480         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26481             this.setValue(new Date(t.dateValue));
26482             this.fireEvent("select", this, this.value);
26483         }
26484     },
26485
26486     // private
26487     selectToday : function(){
26488         this.setValue(new Date().clearTime());
26489         this.fireEvent("select", this, this.value);
26490     },
26491
26492     // private
26493     update : function(date)
26494     {
26495         var vd = this.activeDate;
26496         this.activeDate = date;
26497         if(vd && this.el){
26498             var t = date.getTime();
26499             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26500                 this.cells.removeClass("x-date-selected");
26501                 this.cells.each(function(c){
26502                    if(c.dom.firstChild.dateValue == t){
26503                        c.addClass("x-date-selected");
26504                        setTimeout(function(){
26505                             try{c.dom.firstChild.focus();}catch(e){}
26506                        }, 50);
26507                        return false;
26508                    }
26509                 });
26510                 return;
26511             }
26512         }
26513         
26514         var days = date.getDaysInMonth();
26515         var firstOfMonth = date.getFirstDateOfMonth();
26516         var startingPos = firstOfMonth.getDay()-this.startDay;
26517
26518         if(startingPos <= this.startDay){
26519             startingPos += 7;
26520         }
26521
26522         var pm = date.add("mo", -1);
26523         var prevStart = pm.getDaysInMonth()-startingPos;
26524
26525         var cells = this.cells.elements;
26526         var textEls = this.textNodes;
26527         days += startingPos;
26528
26529         // convert everything to numbers so it's fast
26530         var day = 86400000;
26531         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26532         var today = new Date().clearTime().getTime();
26533         var sel = date.clearTime().getTime();
26534         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26535         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26536         var ddMatch = this.disabledDatesRE;
26537         var ddText = this.disabledDatesText;
26538         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26539         var ddaysText = this.disabledDaysText;
26540         var format = this.format;
26541
26542         var setCellClass = function(cal, cell){
26543             cell.title = "";
26544             var t = d.getTime();
26545             cell.firstChild.dateValue = t;
26546             if(t == today){
26547                 cell.className += " x-date-today";
26548                 cell.title = cal.todayText;
26549             }
26550             if(t == sel){
26551                 cell.className += " x-date-selected";
26552                 setTimeout(function(){
26553                     try{cell.firstChild.focus();}catch(e){}
26554                 }, 50);
26555             }
26556             // disabling
26557             if(t < min) {
26558                 cell.className = " x-date-disabled";
26559                 cell.title = cal.minText;
26560                 return;
26561             }
26562             if(t > max) {
26563                 cell.className = " x-date-disabled";
26564                 cell.title = cal.maxText;
26565                 return;
26566             }
26567             if(ddays){
26568                 if(ddays.indexOf(d.getDay()) != -1){
26569                     cell.title = ddaysText;
26570                     cell.className = " x-date-disabled";
26571                 }
26572             }
26573             if(ddMatch && format){
26574                 var fvalue = d.dateFormat(format);
26575                 if(ddMatch.test(fvalue)){
26576                     cell.title = ddText.replace("%0", fvalue);
26577                     cell.className = " x-date-disabled";
26578                 }
26579             }
26580         };
26581
26582         var i = 0;
26583         for(; i < startingPos; i++) {
26584             textEls[i].innerHTML = (++prevStart);
26585             d.setDate(d.getDate()+1);
26586             cells[i].className = "x-date-prevday";
26587             setCellClass(this, cells[i]);
26588         }
26589         for(; i < days; i++){
26590             intDay = i - startingPos + 1;
26591             textEls[i].innerHTML = (intDay);
26592             d.setDate(d.getDate()+1);
26593             cells[i].className = "x-date-active";
26594             setCellClass(this, cells[i]);
26595         }
26596         var extraDays = 0;
26597         for(; i < 42; i++) {
26598              textEls[i].innerHTML = (++extraDays);
26599              d.setDate(d.getDate()+1);
26600              cells[i].className = "x-date-nextday";
26601              setCellClass(this, cells[i]);
26602         }
26603
26604         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26605         this.fireEvent('monthchange', this, date);
26606         
26607         if(!this.internalRender){
26608             var main = this.el.dom.firstChild;
26609             var w = main.offsetWidth;
26610             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26611             Roo.fly(main).setWidth(w);
26612             this.internalRender = true;
26613             // opera does not respect the auto grow header center column
26614             // then, after it gets a width opera refuses to recalculate
26615             // without a second pass
26616             if(Roo.isOpera && !this.secondPass){
26617                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26618                 this.secondPass = true;
26619                 this.update.defer(10, this, [date]);
26620             }
26621         }
26622         
26623         
26624     }
26625 });        /*
26626  * Based on:
26627  * Ext JS Library 1.1.1
26628  * Copyright(c) 2006-2007, Ext JS, LLC.
26629  *
26630  * Originally Released Under LGPL - original licence link has changed is not relivant.
26631  *
26632  * Fork - LGPL
26633  * <script type="text/javascript">
26634  */
26635 /**
26636  * @class Roo.TabPanel
26637  * @extends Roo.util.Observable
26638  * A lightweight tab container.
26639  * <br><br>
26640  * Usage:
26641  * <pre><code>
26642 // basic tabs 1, built from existing content
26643 var tabs = new Roo.TabPanel("tabs1");
26644 tabs.addTab("script", "View Script");
26645 tabs.addTab("markup", "View Markup");
26646 tabs.activate("script");
26647
26648 // more advanced tabs, built from javascript
26649 var jtabs = new Roo.TabPanel("jtabs");
26650 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26651
26652 // set up the UpdateManager
26653 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26654 var updater = tab2.getUpdateManager();
26655 updater.setDefaultUrl("ajax1.htm");
26656 tab2.on('activate', updater.refresh, updater, true);
26657
26658 // Use setUrl for Ajax loading
26659 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26660 tab3.setUrl("ajax2.htm", null, true);
26661
26662 // Disabled tab
26663 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26664 tab4.disable();
26665
26666 jtabs.activate("jtabs-1");
26667  * </code></pre>
26668  * @constructor
26669  * Create a new TabPanel.
26670  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26671  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26672  */
26673 Roo.TabPanel = function(container, config){
26674     /**
26675     * The container element for this TabPanel.
26676     * @type Roo.Element
26677     */
26678     this.el = Roo.get(container, true);
26679     if(config){
26680         if(typeof config == "boolean"){
26681             this.tabPosition = config ? "bottom" : "top";
26682         }else{
26683             Roo.apply(this, config);
26684         }
26685     }
26686     if(this.tabPosition == "bottom"){
26687         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26688         this.el.addClass("x-tabs-bottom");
26689     }
26690     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26691     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26692     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26693     if(Roo.isIE){
26694         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26695     }
26696     if(this.tabPosition != "bottom"){
26697         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26698          * @type Roo.Element
26699          */
26700         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26701         this.el.addClass("x-tabs-top");
26702     }
26703     this.items = [];
26704
26705     this.bodyEl.setStyle("position", "relative");
26706
26707     this.active = null;
26708     this.activateDelegate = this.activate.createDelegate(this);
26709
26710     this.addEvents({
26711         /**
26712          * @event tabchange
26713          * Fires when the active tab changes
26714          * @param {Roo.TabPanel} this
26715          * @param {Roo.TabPanelItem} activePanel The new active tab
26716          */
26717         "tabchange": true,
26718         /**
26719          * @event beforetabchange
26720          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26721          * @param {Roo.TabPanel} this
26722          * @param {Object} e Set cancel to true on this object to cancel the tab change
26723          * @param {Roo.TabPanelItem} tab The tab being changed to
26724          */
26725         "beforetabchange" : true
26726     });
26727
26728     Roo.EventManager.onWindowResize(this.onResize, this);
26729     this.cpad = this.el.getPadding("lr");
26730     this.hiddenCount = 0;
26731
26732
26733     // toolbar on the tabbar support...
26734     if (this.toolbar) {
26735         var tcfg = this.toolbar;
26736         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26737         this.toolbar = new Roo.Toolbar(tcfg);
26738         if (Roo.isSafari) {
26739             var tbl = tcfg.container.child('table', true);
26740             tbl.setAttribute('width', '100%');
26741         }
26742         
26743     }
26744    
26745
26746
26747     Roo.TabPanel.superclass.constructor.call(this);
26748 };
26749
26750 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26751     /*
26752      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26753      */
26754     tabPosition : "top",
26755     /*
26756      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26757      */
26758     currentTabWidth : 0,
26759     /*
26760      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26761      */
26762     minTabWidth : 40,
26763     /*
26764      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26765      */
26766     maxTabWidth : 250,
26767     /*
26768      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26769      */
26770     preferredTabWidth : 175,
26771     /*
26772      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26773      */
26774     resizeTabs : false,
26775     /*
26776      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26777      */
26778     monitorResize : true,
26779     /*
26780      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26781      */
26782     toolbar : false,
26783
26784     /**
26785      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26786      * @param {String} id The id of the div to use <b>or create</b>
26787      * @param {String} text The text for the tab
26788      * @param {String} content (optional) Content to put in the TabPanelItem body
26789      * @param {Boolean} closable (optional) True to create a close icon on the tab
26790      * @return {Roo.TabPanelItem} The created TabPanelItem
26791      */
26792     addTab : function(id, text, content, closable){
26793         var item = new Roo.TabPanelItem(this, id, text, closable);
26794         this.addTabItem(item);
26795         if(content){
26796             item.setContent(content);
26797         }
26798         return item;
26799     },
26800
26801     /**
26802      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26803      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26804      * @return {Roo.TabPanelItem}
26805      */
26806     getTab : function(id){
26807         return this.items[id];
26808     },
26809
26810     /**
26811      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26812      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26813      */
26814     hideTab : function(id){
26815         var t = this.items[id];
26816         if(!t.isHidden()){
26817            t.setHidden(true);
26818            this.hiddenCount++;
26819            this.autoSizeTabs();
26820         }
26821     },
26822
26823     /**
26824      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26825      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26826      */
26827     unhideTab : function(id){
26828         var t = this.items[id];
26829         if(t.isHidden()){
26830            t.setHidden(false);
26831            this.hiddenCount--;
26832            this.autoSizeTabs();
26833         }
26834     },
26835
26836     /**
26837      * Adds an existing {@link Roo.TabPanelItem}.
26838      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26839      */
26840     addTabItem : function(item){
26841         this.items[item.id] = item;
26842         this.items.push(item);
26843         if(this.resizeTabs){
26844            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26845            this.autoSizeTabs();
26846         }else{
26847             item.autoSize();
26848         }
26849     },
26850
26851     /**
26852      * Removes a {@link Roo.TabPanelItem}.
26853      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26854      */
26855     removeTab : function(id){
26856         var items = this.items;
26857         var tab = items[id];
26858         if(!tab) { return; }
26859         var index = items.indexOf(tab);
26860         if(this.active == tab && items.length > 1){
26861             var newTab = this.getNextAvailable(index);
26862             if(newTab) {
26863                 newTab.activate();
26864             }
26865         }
26866         this.stripEl.dom.removeChild(tab.pnode.dom);
26867         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26868             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26869         }
26870         items.splice(index, 1);
26871         delete this.items[tab.id];
26872         tab.fireEvent("close", tab);
26873         tab.purgeListeners();
26874         this.autoSizeTabs();
26875     },
26876
26877     getNextAvailable : function(start){
26878         var items = this.items;
26879         var index = start;
26880         // look for a next tab that will slide over to
26881         // replace the one being removed
26882         while(index < items.length){
26883             var item = items[++index];
26884             if(item && !item.isHidden()){
26885                 return item;
26886             }
26887         }
26888         // if one isn't found select the previous tab (on the left)
26889         index = start;
26890         while(index >= 0){
26891             var item = items[--index];
26892             if(item && !item.isHidden()){
26893                 return item;
26894             }
26895         }
26896         return null;
26897     },
26898
26899     /**
26900      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26901      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26902      */
26903     disableTab : function(id){
26904         var tab = this.items[id];
26905         if(tab && this.active != tab){
26906             tab.disable();
26907         }
26908     },
26909
26910     /**
26911      * Enables a {@link Roo.TabPanelItem} that is disabled.
26912      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26913      */
26914     enableTab : function(id){
26915         var tab = this.items[id];
26916         tab.enable();
26917     },
26918
26919     /**
26920      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26921      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26922      * @return {Roo.TabPanelItem} The TabPanelItem.
26923      */
26924     activate : function(id){
26925         var tab = this.items[id];
26926         if(!tab){
26927             return null;
26928         }
26929         if(tab == this.active || tab.disabled){
26930             return tab;
26931         }
26932         var e = {};
26933         this.fireEvent("beforetabchange", this, e, tab);
26934         if(e.cancel !== true && !tab.disabled){
26935             if(this.active){
26936                 this.active.hide();
26937             }
26938             this.active = this.items[id];
26939             this.active.show();
26940             this.fireEvent("tabchange", this, this.active);
26941         }
26942         return tab;
26943     },
26944
26945     /**
26946      * Gets the active {@link Roo.TabPanelItem}.
26947      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26948      */
26949     getActiveTab : function(){
26950         return this.active;
26951     },
26952
26953     /**
26954      * Updates the tab body element to fit the height of the container element
26955      * for overflow scrolling
26956      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26957      */
26958     syncHeight : function(targetHeight){
26959         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26960         var bm = this.bodyEl.getMargins();
26961         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26962         this.bodyEl.setHeight(newHeight);
26963         return newHeight;
26964     },
26965
26966     onResize : function(){
26967         if(this.monitorResize){
26968             this.autoSizeTabs();
26969         }
26970     },
26971
26972     /**
26973      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26974      */
26975     beginUpdate : function(){
26976         this.updating = true;
26977     },
26978
26979     /**
26980      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26981      */
26982     endUpdate : function(){
26983         this.updating = false;
26984         this.autoSizeTabs();
26985     },
26986
26987     /**
26988      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26989      */
26990     autoSizeTabs : function(){
26991         var count = this.items.length;
26992         var vcount = count - this.hiddenCount;
26993         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26994         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26995         var availWidth = Math.floor(w / vcount);
26996         var b = this.stripBody;
26997         if(b.getWidth() > w){
26998             var tabs = this.items;
26999             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27000             if(availWidth < this.minTabWidth){
27001                 /*if(!this.sleft){    // incomplete scrolling code
27002                     this.createScrollButtons();
27003                 }
27004                 this.showScroll();
27005                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27006             }
27007         }else{
27008             if(this.currentTabWidth < this.preferredTabWidth){
27009                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27010             }
27011         }
27012     },
27013
27014     /**
27015      * Returns the number of tabs in this TabPanel.
27016      * @return {Number}
27017      */
27018      getCount : function(){
27019          return this.items.length;
27020      },
27021
27022     /**
27023      * Resizes all the tabs to the passed width
27024      * @param {Number} The new width
27025      */
27026     setTabWidth : function(width){
27027         this.currentTabWidth = width;
27028         for(var i = 0, len = this.items.length; i < len; i++) {
27029                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27030         }
27031     },
27032
27033     /**
27034      * Destroys this TabPanel
27035      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27036      */
27037     destroy : function(removeEl){
27038         Roo.EventManager.removeResizeListener(this.onResize, this);
27039         for(var i = 0, len = this.items.length; i < len; i++){
27040             this.items[i].purgeListeners();
27041         }
27042         if(removeEl === true){
27043             this.el.update("");
27044             this.el.remove();
27045         }
27046     }
27047 });
27048
27049 /**
27050  * @class Roo.TabPanelItem
27051  * @extends Roo.util.Observable
27052  * Represents an individual item (tab plus body) in a TabPanel.
27053  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27054  * @param {String} id The id of this TabPanelItem
27055  * @param {String} text The text for the tab of this TabPanelItem
27056  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27057  */
27058 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27059     /**
27060      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27061      * @type Roo.TabPanel
27062      */
27063     this.tabPanel = tabPanel;
27064     /**
27065      * The id for this TabPanelItem
27066      * @type String
27067      */
27068     this.id = id;
27069     /** @private */
27070     this.disabled = false;
27071     /** @private */
27072     this.text = text;
27073     /** @private */
27074     this.loaded = false;
27075     this.closable = closable;
27076
27077     /**
27078      * The body element for this TabPanelItem.
27079      * @type Roo.Element
27080      */
27081     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27082     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27083     this.bodyEl.setStyle("display", "block");
27084     this.bodyEl.setStyle("zoom", "1");
27085     this.hideAction();
27086
27087     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27088     /** @private */
27089     this.el = Roo.get(els.el, true);
27090     this.inner = Roo.get(els.inner, true);
27091     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27092     this.pnode = Roo.get(els.el.parentNode, true);
27093     this.el.on("mousedown", this.onTabMouseDown, this);
27094     this.el.on("click", this.onTabClick, this);
27095     /** @private */
27096     if(closable){
27097         var c = Roo.get(els.close, true);
27098         c.dom.title = this.closeText;
27099         c.addClassOnOver("close-over");
27100         c.on("click", this.closeClick, this);
27101      }
27102
27103     this.addEvents({
27104          /**
27105          * @event activate
27106          * Fires when this tab becomes the active tab.
27107          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27108          * @param {Roo.TabPanelItem} this
27109          */
27110         "activate": true,
27111         /**
27112          * @event beforeclose
27113          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27114          * @param {Roo.TabPanelItem} this
27115          * @param {Object} e Set cancel to true on this object to cancel the close.
27116          */
27117         "beforeclose": true,
27118         /**
27119          * @event close
27120          * Fires when this tab is closed.
27121          * @param {Roo.TabPanelItem} this
27122          */
27123          "close": true,
27124         /**
27125          * @event deactivate
27126          * Fires when this tab is no longer the active tab.
27127          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27128          * @param {Roo.TabPanelItem} this
27129          */
27130          "deactivate" : true
27131     });
27132     this.hidden = false;
27133
27134     Roo.TabPanelItem.superclass.constructor.call(this);
27135 };
27136
27137 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27138     purgeListeners : function(){
27139        Roo.util.Observable.prototype.purgeListeners.call(this);
27140        this.el.removeAllListeners();
27141     },
27142     /**
27143      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27144      */
27145     show : function(){
27146         this.pnode.addClass("on");
27147         this.showAction();
27148         if(Roo.isOpera){
27149             this.tabPanel.stripWrap.repaint();
27150         }
27151         this.fireEvent("activate", this.tabPanel, this);
27152     },
27153
27154     /**
27155      * Returns true if this tab is the active tab.
27156      * @return {Boolean}
27157      */
27158     isActive : function(){
27159         return this.tabPanel.getActiveTab() == this;
27160     },
27161
27162     /**
27163      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27164      */
27165     hide : function(){
27166         this.pnode.removeClass("on");
27167         this.hideAction();
27168         this.fireEvent("deactivate", this.tabPanel, this);
27169     },
27170
27171     hideAction : function(){
27172         this.bodyEl.hide();
27173         this.bodyEl.setStyle("position", "absolute");
27174         this.bodyEl.setLeft("-20000px");
27175         this.bodyEl.setTop("-20000px");
27176     },
27177
27178     showAction : function(){
27179         this.bodyEl.setStyle("position", "relative");
27180         this.bodyEl.setTop("");
27181         this.bodyEl.setLeft("");
27182         this.bodyEl.show();
27183     },
27184
27185     /**
27186      * Set the tooltip for the tab.
27187      * @param {String} tooltip The tab's tooltip
27188      */
27189     setTooltip : function(text){
27190         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27191             this.textEl.dom.qtip = text;
27192             this.textEl.dom.removeAttribute('title');
27193         }else{
27194             this.textEl.dom.title = text;
27195         }
27196     },
27197
27198     onTabClick : function(e){
27199         e.preventDefault();
27200         this.tabPanel.activate(this.id);
27201     },
27202
27203     onTabMouseDown : function(e){
27204         e.preventDefault();
27205         this.tabPanel.activate(this.id);
27206     },
27207
27208     getWidth : function(){
27209         return this.inner.getWidth();
27210     },
27211
27212     setWidth : function(width){
27213         var iwidth = width - this.pnode.getPadding("lr");
27214         this.inner.setWidth(iwidth);
27215         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27216         this.pnode.setWidth(width);
27217     },
27218
27219     /**
27220      * Show or hide the tab
27221      * @param {Boolean} hidden True to hide or false to show.
27222      */
27223     setHidden : function(hidden){
27224         this.hidden = hidden;
27225         this.pnode.setStyle("display", hidden ? "none" : "");
27226     },
27227
27228     /**
27229      * Returns true if this tab is "hidden"
27230      * @return {Boolean}
27231      */
27232     isHidden : function(){
27233         return this.hidden;
27234     },
27235
27236     /**
27237      * Returns the text for this tab
27238      * @return {String}
27239      */
27240     getText : function(){
27241         return this.text;
27242     },
27243
27244     autoSize : function(){
27245         //this.el.beginMeasure();
27246         this.textEl.setWidth(1);
27247         /*
27248          *  #2804 [new] Tabs in Roojs
27249          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27250          */
27251         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27252         //this.el.endMeasure();
27253     },
27254
27255     /**
27256      * Sets the text for the tab (Note: this also sets the tooltip text)
27257      * @param {String} text The tab's text and tooltip
27258      */
27259     setText : function(text){
27260         this.text = text;
27261         this.textEl.update(text);
27262         this.setTooltip(text);
27263         if(!this.tabPanel.resizeTabs){
27264             this.autoSize();
27265         }
27266     },
27267     /**
27268      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27269      */
27270     activate : function(){
27271         this.tabPanel.activate(this.id);
27272     },
27273
27274     /**
27275      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27276      */
27277     disable : function(){
27278         if(this.tabPanel.active != this){
27279             this.disabled = true;
27280             this.pnode.addClass("disabled");
27281         }
27282     },
27283
27284     /**
27285      * Enables this TabPanelItem if it was previously disabled.
27286      */
27287     enable : function(){
27288         this.disabled = false;
27289         this.pnode.removeClass("disabled");
27290     },
27291
27292     /**
27293      * Sets the content for this TabPanelItem.
27294      * @param {String} content The content
27295      * @param {Boolean} loadScripts true to look for and load scripts
27296      */
27297     setContent : function(content, loadScripts){
27298         this.bodyEl.update(content, loadScripts);
27299     },
27300
27301     /**
27302      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27303      * @return {Roo.UpdateManager} The UpdateManager
27304      */
27305     getUpdateManager : function(){
27306         return this.bodyEl.getUpdateManager();
27307     },
27308
27309     /**
27310      * Set a URL to be used to load the content for this TabPanelItem.
27311      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27312      * @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)
27313      * @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)
27314      * @return {Roo.UpdateManager} The UpdateManager
27315      */
27316     setUrl : function(url, params, loadOnce){
27317         if(this.refreshDelegate){
27318             this.un('activate', this.refreshDelegate);
27319         }
27320         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27321         this.on("activate", this.refreshDelegate);
27322         return this.bodyEl.getUpdateManager();
27323     },
27324
27325     /** @private */
27326     _handleRefresh : function(url, params, loadOnce){
27327         if(!loadOnce || !this.loaded){
27328             var updater = this.bodyEl.getUpdateManager();
27329             updater.update(url, params, this._setLoaded.createDelegate(this));
27330         }
27331     },
27332
27333     /**
27334      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27335      *   Will fail silently if the setUrl method has not been called.
27336      *   This does not activate the panel, just updates its content.
27337      */
27338     refresh : function(){
27339         if(this.refreshDelegate){
27340            this.loaded = false;
27341            this.refreshDelegate();
27342         }
27343     },
27344
27345     /** @private */
27346     _setLoaded : function(){
27347         this.loaded = true;
27348     },
27349
27350     /** @private */
27351     closeClick : function(e){
27352         var o = {};
27353         e.stopEvent();
27354         this.fireEvent("beforeclose", this, o);
27355         if(o.cancel !== true){
27356             this.tabPanel.removeTab(this.id);
27357         }
27358     },
27359     /**
27360      * The text displayed in the tooltip for the close icon.
27361      * @type String
27362      */
27363     closeText : "Close this tab"
27364 });
27365
27366 /** @private */
27367 Roo.TabPanel.prototype.createStrip = function(container){
27368     var strip = document.createElement("div");
27369     strip.className = "x-tabs-wrap";
27370     container.appendChild(strip);
27371     return strip;
27372 };
27373 /** @private */
27374 Roo.TabPanel.prototype.createStripList = function(strip){
27375     // div wrapper for retard IE
27376     // returns the "tr" element.
27377     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27378         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27379         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27380     return strip.firstChild.firstChild.firstChild.firstChild;
27381 };
27382 /** @private */
27383 Roo.TabPanel.prototype.createBody = function(container){
27384     var body = document.createElement("div");
27385     Roo.id(body, "tab-body");
27386     Roo.fly(body).addClass("x-tabs-body");
27387     container.appendChild(body);
27388     return body;
27389 };
27390 /** @private */
27391 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27392     var body = Roo.getDom(id);
27393     if(!body){
27394         body = document.createElement("div");
27395         body.id = id;
27396     }
27397     Roo.fly(body).addClass("x-tabs-item-body");
27398     bodyEl.insertBefore(body, bodyEl.firstChild);
27399     return body;
27400 };
27401 /** @private */
27402 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27403     var td = document.createElement("td");
27404     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27405     //stripEl.appendChild(td);
27406     if(closable){
27407         td.className = "x-tabs-closable";
27408         if(!this.closeTpl){
27409             this.closeTpl = new Roo.Template(
27410                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27411                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27412                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27413             );
27414         }
27415         var el = this.closeTpl.overwrite(td, {"text": text});
27416         var close = el.getElementsByTagName("div")[0];
27417         var inner = el.getElementsByTagName("em")[0];
27418         return {"el": el, "close": close, "inner": inner};
27419     } else {
27420         if(!this.tabTpl){
27421             this.tabTpl = new Roo.Template(
27422                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27423                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27424             );
27425         }
27426         var el = this.tabTpl.overwrite(td, {"text": text});
27427         var inner = el.getElementsByTagName("em")[0];
27428         return {"el": el, "inner": inner};
27429     }
27430 };/*
27431  * Based on:
27432  * Ext JS Library 1.1.1
27433  * Copyright(c) 2006-2007, Ext JS, LLC.
27434  *
27435  * Originally Released Under LGPL - original licence link has changed is not relivant.
27436  *
27437  * Fork - LGPL
27438  * <script type="text/javascript">
27439  */
27440
27441 /**
27442  * @class Roo.Button
27443  * @extends Roo.util.Observable
27444  * Simple Button class
27445  * @cfg {String} text The button text
27446  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27447  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27448  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27449  * @cfg {Object} scope The scope of the handler
27450  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27451  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27452  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27453  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27454  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27455  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27456    applies if enableToggle = true)
27457  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27458  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27459   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27460  * @constructor
27461  * Create a new button
27462  * @param {Object} config The config object
27463  */
27464 Roo.Button = function(renderTo, config)
27465 {
27466     if (!config) {
27467         config = renderTo;
27468         renderTo = config.renderTo || false;
27469     }
27470     
27471     Roo.apply(this, config);
27472     this.addEvents({
27473         /**
27474              * @event click
27475              * Fires when this button is clicked
27476              * @param {Button} this
27477              * @param {EventObject} e The click event
27478              */
27479             "click" : true,
27480         /**
27481              * @event toggle
27482              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27483              * @param {Button} this
27484              * @param {Boolean} pressed
27485              */
27486             "toggle" : true,
27487         /**
27488              * @event mouseover
27489              * Fires when the mouse hovers over the button
27490              * @param {Button} this
27491              * @param {Event} e The event object
27492              */
27493         'mouseover' : true,
27494         /**
27495              * @event mouseout
27496              * Fires when the mouse exits the button
27497              * @param {Button} this
27498              * @param {Event} e The event object
27499              */
27500         'mouseout': true,
27501          /**
27502              * @event render
27503              * Fires when the button is rendered
27504              * @param {Button} this
27505              */
27506         'render': true
27507     });
27508     if(this.menu){
27509         this.menu = Roo.menu.MenuMgr.get(this.menu);
27510     }
27511     // register listeners first!!  - so render can be captured..
27512     Roo.util.Observable.call(this);
27513     if(renderTo){
27514         this.render(renderTo);
27515     }
27516     
27517   
27518 };
27519
27520 Roo.extend(Roo.Button, Roo.util.Observable, {
27521     /**
27522      * 
27523      */
27524     
27525     /**
27526      * Read-only. True if this button is hidden
27527      * @type Boolean
27528      */
27529     hidden : false,
27530     /**
27531      * Read-only. True if this button is disabled
27532      * @type Boolean
27533      */
27534     disabled : false,
27535     /**
27536      * Read-only. True if this button is pressed (only if enableToggle = true)
27537      * @type Boolean
27538      */
27539     pressed : false,
27540
27541     /**
27542      * @cfg {Number} tabIndex 
27543      * The DOM tabIndex for this button (defaults to undefined)
27544      */
27545     tabIndex : undefined,
27546
27547     /**
27548      * @cfg {Boolean} enableToggle
27549      * True to enable pressed/not pressed toggling (defaults to false)
27550      */
27551     enableToggle: false,
27552     /**
27553      * @cfg {Mixed} menu
27554      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27555      */
27556     menu : undefined,
27557     /**
27558      * @cfg {String} menuAlign
27559      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27560      */
27561     menuAlign : "tl-bl?",
27562
27563     /**
27564      * @cfg {String} iconCls
27565      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27566      */
27567     iconCls : undefined,
27568     /**
27569      * @cfg {String} type
27570      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27571      */
27572     type : 'button',
27573
27574     // private
27575     menuClassTarget: 'tr',
27576
27577     /**
27578      * @cfg {String} clickEvent
27579      * The type of event to map to the button's event handler (defaults to 'click')
27580      */
27581     clickEvent : 'click',
27582
27583     /**
27584      * @cfg {Boolean} handleMouseEvents
27585      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27586      */
27587     handleMouseEvents : true,
27588
27589     /**
27590      * @cfg {String} tooltipType
27591      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27592      */
27593     tooltipType : 'qtip',
27594
27595     /**
27596      * @cfg {String} cls
27597      * A CSS class to apply to the button's main element.
27598      */
27599     
27600     /**
27601      * @cfg {Roo.Template} template (Optional)
27602      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27603      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27604      * require code modifications if required elements (e.g. a button) aren't present.
27605      */
27606
27607     // private
27608     render : function(renderTo){
27609         var btn;
27610         if(this.hideParent){
27611             this.parentEl = Roo.get(renderTo);
27612         }
27613         if(!this.dhconfig){
27614             if(!this.template){
27615                 if(!Roo.Button.buttonTemplate){
27616                     // hideous table template
27617                     Roo.Button.buttonTemplate = new Roo.Template(
27618                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27619                         '<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>',
27620                         "</tr></tbody></table>");
27621                 }
27622                 this.template = Roo.Button.buttonTemplate;
27623             }
27624             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27625             var btnEl = btn.child("button:first");
27626             btnEl.on('focus', this.onFocus, this);
27627             btnEl.on('blur', this.onBlur, this);
27628             if(this.cls){
27629                 btn.addClass(this.cls);
27630             }
27631             if(this.icon){
27632                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27633             }
27634             if(this.iconCls){
27635                 btnEl.addClass(this.iconCls);
27636                 if(!this.cls){
27637                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27638                 }
27639             }
27640             if(this.tabIndex !== undefined){
27641                 btnEl.dom.tabIndex = this.tabIndex;
27642             }
27643             if(this.tooltip){
27644                 if(typeof this.tooltip == 'object'){
27645                     Roo.QuickTips.tips(Roo.apply({
27646                           target: btnEl.id
27647                     }, this.tooltip));
27648                 } else {
27649                     btnEl.dom[this.tooltipType] = this.tooltip;
27650                 }
27651             }
27652         }else{
27653             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27654         }
27655         this.el = btn;
27656         if(this.id){
27657             this.el.dom.id = this.el.id = this.id;
27658         }
27659         if(this.menu){
27660             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27661             this.menu.on("show", this.onMenuShow, this);
27662             this.menu.on("hide", this.onMenuHide, this);
27663         }
27664         btn.addClass("x-btn");
27665         if(Roo.isIE && !Roo.isIE7){
27666             this.autoWidth.defer(1, this);
27667         }else{
27668             this.autoWidth();
27669         }
27670         if(this.handleMouseEvents){
27671             btn.on("mouseover", this.onMouseOver, this);
27672             btn.on("mouseout", this.onMouseOut, this);
27673             btn.on("mousedown", this.onMouseDown, this);
27674         }
27675         btn.on(this.clickEvent, this.onClick, this);
27676         //btn.on("mouseup", this.onMouseUp, this);
27677         if(this.hidden){
27678             this.hide();
27679         }
27680         if(this.disabled){
27681             this.disable();
27682         }
27683         Roo.ButtonToggleMgr.register(this);
27684         if(this.pressed){
27685             this.el.addClass("x-btn-pressed");
27686         }
27687         if(this.repeat){
27688             var repeater = new Roo.util.ClickRepeater(btn,
27689                 typeof this.repeat == "object" ? this.repeat : {}
27690             );
27691             repeater.on("click", this.onClick,  this);
27692         }
27693         
27694         this.fireEvent('render', this);
27695         
27696     },
27697     /**
27698      * Returns the button's underlying element
27699      * @return {Roo.Element} The element
27700      */
27701     getEl : function(){
27702         return this.el;  
27703     },
27704     
27705     /**
27706      * Destroys this Button and removes any listeners.
27707      */
27708     destroy : function(){
27709         Roo.ButtonToggleMgr.unregister(this);
27710         this.el.removeAllListeners();
27711         this.purgeListeners();
27712         this.el.remove();
27713     },
27714
27715     // private
27716     autoWidth : function(){
27717         if(this.el){
27718             this.el.setWidth("auto");
27719             if(Roo.isIE7 && Roo.isStrict){
27720                 var ib = this.el.child('button');
27721                 if(ib && ib.getWidth() > 20){
27722                     ib.clip();
27723                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27724                 }
27725             }
27726             if(this.minWidth){
27727                 if(this.hidden){
27728                     this.el.beginMeasure();
27729                 }
27730                 if(this.el.getWidth() < this.minWidth){
27731                     this.el.setWidth(this.minWidth);
27732                 }
27733                 if(this.hidden){
27734                     this.el.endMeasure();
27735                 }
27736             }
27737         }
27738     },
27739
27740     /**
27741      * Assigns this button's click handler
27742      * @param {Function} handler The function to call when the button is clicked
27743      * @param {Object} scope (optional) Scope for the function passed in
27744      */
27745     setHandler : function(handler, scope){
27746         this.handler = handler;
27747         this.scope = scope;  
27748     },
27749     
27750     /**
27751      * Sets this button's text
27752      * @param {String} text The button text
27753      */
27754     setText : function(text){
27755         this.text = text;
27756         if(this.el){
27757             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27758         }
27759         this.autoWidth();
27760     },
27761     
27762     /**
27763      * Gets the text for this button
27764      * @return {String} The button text
27765      */
27766     getText : function(){
27767         return this.text;  
27768     },
27769     
27770     /**
27771      * Show this button
27772      */
27773     show: function(){
27774         this.hidden = false;
27775         if(this.el){
27776             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27777         }
27778     },
27779     
27780     /**
27781      * Hide this button
27782      */
27783     hide: function(){
27784         this.hidden = true;
27785         if(this.el){
27786             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27787         }
27788     },
27789     
27790     /**
27791      * Convenience function for boolean show/hide
27792      * @param {Boolean} visible True to show, false to hide
27793      */
27794     setVisible: function(visible){
27795         if(visible) {
27796             this.show();
27797         }else{
27798             this.hide();
27799         }
27800     },
27801     
27802     /**
27803      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27804      * @param {Boolean} state (optional) Force a particular state
27805      */
27806     toggle : function(state){
27807         state = state === undefined ? !this.pressed : state;
27808         if(state != this.pressed){
27809             if(state){
27810                 this.el.addClass("x-btn-pressed");
27811                 this.pressed = true;
27812                 this.fireEvent("toggle", this, true);
27813             }else{
27814                 this.el.removeClass("x-btn-pressed");
27815                 this.pressed = false;
27816                 this.fireEvent("toggle", this, false);
27817             }
27818             if(this.toggleHandler){
27819                 this.toggleHandler.call(this.scope || this, this, state);
27820             }
27821         }
27822     },
27823     
27824     /**
27825      * Focus the button
27826      */
27827     focus : function(){
27828         this.el.child('button:first').focus();
27829     },
27830     
27831     /**
27832      * Disable this button
27833      */
27834     disable : function(){
27835         if(this.el){
27836             this.el.addClass("x-btn-disabled");
27837         }
27838         this.disabled = true;
27839     },
27840     
27841     /**
27842      * Enable this button
27843      */
27844     enable : function(){
27845         if(this.el){
27846             this.el.removeClass("x-btn-disabled");
27847         }
27848         this.disabled = false;
27849     },
27850
27851     /**
27852      * Convenience function for boolean enable/disable
27853      * @param {Boolean} enabled True to enable, false to disable
27854      */
27855     setDisabled : function(v){
27856         this[v !== true ? "enable" : "disable"]();
27857     },
27858
27859     // private
27860     onClick : function(e){
27861         if(e){
27862             e.preventDefault();
27863         }
27864         if(e.button != 0){
27865             return;
27866         }
27867         if(!this.disabled){
27868             if(this.enableToggle){
27869                 this.toggle();
27870             }
27871             if(this.menu && !this.menu.isVisible()){
27872                 this.menu.show(this.el, this.menuAlign);
27873             }
27874             this.fireEvent("click", this, e);
27875             if(this.handler){
27876                 this.el.removeClass("x-btn-over");
27877                 this.handler.call(this.scope || this, this, e);
27878             }
27879         }
27880     },
27881     // private
27882     onMouseOver : function(e){
27883         if(!this.disabled){
27884             this.el.addClass("x-btn-over");
27885             this.fireEvent('mouseover', this, e);
27886         }
27887     },
27888     // private
27889     onMouseOut : function(e){
27890         if(!e.within(this.el,  true)){
27891             this.el.removeClass("x-btn-over");
27892             this.fireEvent('mouseout', this, e);
27893         }
27894     },
27895     // private
27896     onFocus : function(e){
27897         if(!this.disabled){
27898             this.el.addClass("x-btn-focus");
27899         }
27900     },
27901     // private
27902     onBlur : function(e){
27903         this.el.removeClass("x-btn-focus");
27904     },
27905     // private
27906     onMouseDown : function(e){
27907         if(!this.disabled && e.button == 0){
27908             this.el.addClass("x-btn-click");
27909             Roo.get(document).on('mouseup', this.onMouseUp, this);
27910         }
27911     },
27912     // private
27913     onMouseUp : function(e){
27914         if(e.button == 0){
27915             this.el.removeClass("x-btn-click");
27916             Roo.get(document).un('mouseup', this.onMouseUp, this);
27917         }
27918     },
27919     // private
27920     onMenuShow : function(e){
27921         this.el.addClass("x-btn-menu-active");
27922     },
27923     // private
27924     onMenuHide : function(e){
27925         this.el.removeClass("x-btn-menu-active");
27926     }   
27927 });
27928
27929 // Private utility class used by Button
27930 Roo.ButtonToggleMgr = function(){
27931    var groups = {};
27932    
27933    function toggleGroup(btn, state){
27934        if(state){
27935            var g = groups[btn.toggleGroup];
27936            for(var i = 0, l = g.length; i < l; i++){
27937                if(g[i] != btn){
27938                    g[i].toggle(false);
27939                }
27940            }
27941        }
27942    }
27943    
27944    return {
27945        register : function(btn){
27946            if(!btn.toggleGroup){
27947                return;
27948            }
27949            var g = groups[btn.toggleGroup];
27950            if(!g){
27951                g = groups[btn.toggleGroup] = [];
27952            }
27953            g.push(btn);
27954            btn.on("toggle", toggleGroup);
27955        },
27956        
27957        unregister : function(btn){
27958            if(!btn.toggleGroup){
27959                return;
27960            }
27961            var g = groups[btn.toggleGroup];
27962            if(g){
27963                g.remove(btn);
27964                btn.un("toggle", toggleGroup);
27965            }
27966        }
27967    };
27968 }();/*
27969  * Based on:
27970  * Ext JS Library 1.1.1
27971  * Copyright(c) 2006-2007, Ext JS, LLC.
27972  *
27973  * Originally Released Under LGPL - original licence link has changed is not relivant.
27974  *
27975  * Fork - LGPL
27976  * <script type="text/javascript">
27977  */
27978  
27979 /**
27980  * @class Roo.SplitButton
27981  * @extends Roo.Button
27982  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27983  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27984  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27985  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27986  * @cfg {String} arrowTooltip The title attribute of the arrow
27987  * @constructor
27988  * Create a new menu button
27989  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27990  * @param {Object} config The config object
27991  */
27992 Roo.SplitButton = function(renderTo, config){
27993     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27994     /**
27995      * @event arrowclick
27996      * Fires when this button's arrow is clicked
27997      * @param {SplitButton} this
27998      * @param {EventObject} e The click event
27999      */
28000     this.addEvents({"arrowclick":true});
28001 };
28002
28003 Roo.extend(Roo.SplitButton, Roo.Button, {
28004     render : function(renderTo){
28005         // this is one sweet looking template!
28006         var tpl = new Roo.Template(
28007             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28008             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28009             '<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>',
28010             "</tbody></table></td><td>",
28011             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28012             '<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>',
28013             "</tbody></table></td></tr></table>"
28014         );
28015         var btn = tpl.append(renderTo, [this.text, this.type], true);
28016         var btnEl = btn.child("button");
28017         if(this.cls){
28018             btn.addClass(this.cls);
28019         }
28020         if(this.icon){
28021             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28022         }
28023         if(this.iconCls){
28024             btnEl.addClass(this.iconCls);
28025             if(!this.cls){
28026                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28027             }
28028         }
28029         this.el = btn;
28030         if(this.handleMouseEvents){
28031             btn.on("mouseover", this.onMouseOver, this);
28032             btn.on("mouseout", this.onMouseOut, this);
28033             btn.on("mousedown", this.onMouseDown, this);
28034             btn.on("mouseup", this.onMouseUp, this);
28035         }
28036         btn.on(this.clickEvent, this.onClick, this);
28037         if(this.tooltip){
28038             if(typeof this.tooltip == 'object'){
28039                 Roo.QuickTips.tips(Roo.apply({
28040                       target: btnEl.id
28041                 }, this.tooltip));
28042             } else {
28043                 btnEl.dom[this.tooltipType] = this.tooltip;
28044             }
28045         }
28046         if(this.arrowTooltip){
28047             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28048         }
28049         if(this.hidden){
28050             this.hide();
28051         }
28052         if(this.disabled){
28053             this.disable();
28054         }
28055         if(this.pressed){
28056             this.el.addClass("x-btn-pressed");
28057         }
28058         if(Roo.isIE && !Roo.isIE7){
28059             this.autoWidth.defer(1, this);
28060         }else{
28061             this.autoWidth();
28062         }
28063         if(this.menu){
28064             this.menu.on("show", this.onMenuShow, this);
28065             this.menu.on("hide", this.onMenuHide, this);
28066         }
28067         this.fireEvent('render', this);
28068     },
28069
28070     // private
28071     autoWidth : function(){
28072         if(this.el){
28073             var tbl = this.el.child("table:first");
28074             var tbl2 = this.el.child("table:last");
28075             this.el.setWidth("auto");
28076             tbl.setWidth("auto");
28077             if(Roo.isIE7 && Roo.isStrict){
28078                 var ib = this.el.child('button:first');
28079                 if(ib && ib.getWidth() > 20){
28080                     ib.clip();
28081                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28082                 }
28083             }
28084             if(this.minWidth){
28085                 if(this.hidden){
28086                     this.el.beginMeasure();
28087                 }
28088                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28089                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28090                 }
28091                 if(this.hidden){
28092                     this.el.endMeasure();
28093                 }
28094             }
28095             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28096         } 
28097     },
28098     /**
28099      * Sets this button's click handler
28100      * @param {Function} handler The function to call when the button is clicked
28101      * @param {Object} scope (optional) Scope for the function passed above
28102      */
28103     setHandler : function(handler, scope){
28104         this.handler = handler;
28105         this.scope = scope;  
28106     },
28107     
28108     /**
28109      * Sets this button's arrow click handler
28110      * @param {Function} handler The function to call when the arrow is clicked
28111      * @param {Object} scope (optional) Scope for the function passed above
28112      */
28113     setArrowHandler : function(handler, scope){
28114         this.arrowHandler = handler;
28115         this.scope = scope;  
28116     },
28117     
28118     /**
28119      * Focus the button
28120      */
28121     focus : function(){
28122         if(this.el){
28123             this.el.child("button:first").focus();
28124         }
28125     },
28126
28127     // private
28128     onClick : function(e){
28129         e.preventDefault();
28130         if(!this.disabled){
28131             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28132                 if(this.menu && !this.menu.isVisible()){
28133                     this.menu.show(this.el, this.menuAlign);
28134                 }
28135                 this.fireEvent("arrowclick", this, e);
28136                 if(this.arrowHandler){
28137                     this.arrowHandler.call(this.scope || this, this, e);
28138                 }
28139             }else{
28140                 this.fireEvent("click", this, e);
28141                 if(this.handler){
28142                     this.handler.call(this.scope || this, this, e);
28143                 }
28144             }
28145         }
28146     },
28147     // private
28148     onMouseDown : function(e){
28149         if(!this.disabled){
28150             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28151         }
28152     },
28153     // private
28154     onMouseUp : function(e){
28155         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28156     }   
28157 });
28158
28159
28160 // backwards compat
28161 Roo.MenuButton = Roo.SplitButton;/*
28162  * Based on:
28163  * Ext JS Library 1.1.1
28164  * Copyright(c) 2006-2007, Ext JS, LLC.
28165  *
28166  * Originally Released Under LGPL - original licence link has changed is not relivant.
28167  *
28168  * Fork - LGPL
28169  * <script type="text/javascript">
28170  */
28171
28172 /**
28173  * @class Roo.Toolbar
28174  * Basic Toolbar class.
28175  * @constructor
28176  * Creates a new Toolbar
28177  * @param {Object} container The config object
28178  */ 
28179 Roo.Toolbar = function(container, buttons, config)
28180 {
28181     /// old consturctor format still supported..
28182     if(container instanceof Array){ // omit the container for later rendering
28183         buttons = container;
28184         config = buttons;
28185         container = null;
28186     }
28187     if (typeof(container) == 'object' && container.xtype) {
28188         config = container;
28189         container = config.container;
28190         buttons = config.buttons || []; // not really - use items!!
28191     }
28192     var xitems = [];
28193     if (config && config.items) {
28194         xitems = config.items;
28195         delete config.items;
28196     }
28197     Roo.apply(this, config);
28198     this.buttons = buttons;
28199     
28200     if(container){
28201         this.render(container);
28202     }
28203     this.xitems = xitems;
28204     Roo.each(xitems, function(b) {
28205         this.add(b);
28206     }, this);
28207     
28208 };
28209
28210 Roo.Toolbar.prototype = {
28211     /**
28212      * @cfg {Array} items
28213      * array of button configs or elements to add (will be converted to a MixedCollection)
28214      */
28215     
28216     /**
28217      * @cfg {String/HTMLElement/Element} container
28218      * The id or element that will contain the toolbar
28219      */
28220     // private
28221     render : function(ct){
28222         this.el = Roo.get(ct);
28223         if(this.cls){
28224             this.el.addClass(this.cls);
28225         }
28226         // using a table allows for vertical alignment
28227         // 100% width is needed by Safari...
28228         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28229         this.tr = this.el.child("tr", true);
28230         var autoId = 0;
28231         this.items = new Roo.util.MixedCollection(false, function(o){
28232             return o.id || ("item" + (++autoId));
28233         });
28234         if(this.buttons){
28235             this.add.apply(this, this.buttons);
28236             delete this.buttons;
28237         }
28238     },
28239
28240     /**
28241      * Adds element(s) to the toolbar -- this function takes a variable number of 
28242      * arguments of mixed type and adds them to the toolbar.
28243      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28244      * <ul>
28245      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28246      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28247      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28248      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28249      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28250      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28251      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28252      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28253      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28254      * </ul>
28255      * @param {Mixed} arg2
28256      * @param {Mixed} etc.
28257      */
28258     add : function(){
28259         var a = arguments, l = a.length;
28260         for(var i = 0; i < l; i++){
28261             this._add(a[i]);
28262         }
28263     },
28264     // private..
28265     _add : function(el) {
28266         
28267         if (el.xtype) {
28268             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28269         }
28270         
28271         if (el.applyTo){ // some kind of form field
28272             return this.addField(el);
28273         } 
28274         if (el.render){ // some kind of Toolbar.Item
28275             return this.addItem(el);
28276         }
28277         if (typeof el == "string"){ // string
28278             if(el == "separator" || el == "-"){
28279                 return this.addSeparator();
28280             }
28281             if (el == " "){
28282                 return this.addSpacer();
28283             }
28284             if(el == "->"){
28285                 return this.addFill();
28286             }
28287             return this.addText(el);
28288             
28289         }
28290         if(el.tagName){ // element
28291             return this.addElement(el);
28292         }
28293         if(typeof el == "object"){ // must be button config?
28294             return this.addButton(el);
28295         }
28296         // and now what?!?!
28297         return false;
28298         
28299     },
28300     
28301     /**
28302      * Add an Xtype element
28303      * @param {Object} xtype Xtype Object
28304      * @return {Object} created Object
28305      */
28306     addxtype : function(e){
28307         return this.add(e);  
28308     },
28309     
28310     /**
28311      * Returns the Element for this toolbar.
28312      * @return {Roo.Element}
28313      */
28314     getEl : function(){
28315         return this.el;  
28316     },
28317     
28318     /**
28319      * Adds a separator
28320      * @return {Roo.Toolbar.Item} The separator item
28321      */
28322     addSeparator : function(){
28323         return this.addItem(new Roo.Toolbar.Separator());
28324     },
28325
28326     /**
28327      * Adds a spacer element
28328      * @return {Roo.Toolbar.Spacer} The spacer item
28329      */
28330     addSpacer : function(){
28331         return this.addItem(new Roo.Toolbar.Spacer());
28332     },
28333
28334     /**
28335      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28336      * @return {Roo.Toolbar.Fill} The fill item
28337      */
28338     addFill : function(){
28339         return this.addItem(new Roo.Toolbar.Fill());
28340     },
28341
28342     /**
28343      * Adds any standard HTML element to the toolbar
28344      * @param {String/HTMLElement/Element} el The element or id of the element to add
28345      * @return {Roo.Toolbar.Item} The element's item
28346      */
28347     addElement : function(el){
28348         return this.addItem(new Roo.Toolbar.Item(el));
28349     },
28350     /**
28351      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28352      * @type Roo.util.MixedCollection  
28353      */
28354     items : false,
28355      
28356     /**
28357      * Adds any Toolbar.Item or subclass
28358      * @param {Roo.Toolbar.Item} item
28359      * @return {Roo.Toolbar.Item} The item
28360      */
28361     addItem : function(item){
28362         var td = this.nextBlock();
28363         item.render(td);
28364         this.items.add(item);
28365         return item;
28366     },
28367     
28368     /**
28369      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28370      * @param {Object/Array} config A button config or array of configs
28371      * @return {Roo.Toolbar.Button/Array}
28372      */
28373     addButton : function(config){
28374         if(config instanceof Array){
28375             var buttons = [];
28376             for(var i = 0, len = config.length; i < len; i++) {
28377                 buttons.push(this.addButton(config[i]));
28378             }
28379             return buttons;
28380         }
28381         var b = config;
28382         if(!(config instanceof Roo.Toolbar.Button)){
28383             b = config.split ?
28384                 new Roo.Toolbar.SplitButton(config) :
28385                 new Roo.Toolbar.Button(config);
28386         }
28387         var td = this.nextBlock();
28388         b.render(td);
28389         this.items.add(b);
28390         return b;
28391     },
28392     
28393     /**
28394      * Adds text to the toolbar
28395      * @param {String} text The text to add
28396      * @return {Roo.Toolbar.Item} The element's item
28397      */
28398     addText : function(text){
28399         return this.addItem(new Roo.Toolbar.TextItem(text));
28400     },
28401     
28402     /**
28403      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28404      * @param {Number} index The index where the item is to be inserted
28405      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28406      * @return {Roo.Toolbar.Button/Item}
28407      */
28408     insertButton : function(index, item){
28409         if(item instanceof Array){
28410             var buttons = [];
28411             for(var i = 0, len = item.length; i < len; i++) {
28412                buttons.push(this.insertButton(index + i, item[i]));
28413             }
28414             return buttons;
28415         }
28416         if (!(item instanceof Roo.Toolbar.Button)){
28417            item = new Roo.Toolbar.Button(item);
28418         }
28419         var td = document.createElement("td");
28420         this.tr.insertBefore(td, this.tr.childNodes[index]);
28421         item.render(td);
28422         this.items.insert(index, item);
28423         return item;
28424     },
28425     
28426     /**
28427      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28428      * @param {Object} config
28429      * @return {Roo.Toolbar.Item} The element's item
28430      */
28431     addDom : function(config, returnEl){
28432         var td = this.nextBlock();
28433         Roo.DomHelper.overwrite(td, config);
28434         var ti = new Roo.Toolbar.Item(td.firstChild);
28435         ti.render(td);
28436         this.items.add(ti);
28437         return ti;
28438     },
28439
28440     /**
28441      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28442      * @type Roo.util.MixedCollection  
28443      */
28444     fields : false,
28445     
28446     /**
28447      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28448      * Note: the field should not have been rendered yet. For a field that has already been
28449      * rendered, use {@link #addElement}.
28450      * @param {Roo.form.Field} field
28451      * @return {Roo.ToolbarItem}
28452      */
28453      
28454       
28455     addField : function(field) {
28456         if (!this.fields) {
28457             var autoId = 0;
28458             this.fields = new Roo.util.MixedCollection(false, function(o){
28459                 return o.id || ("item" + (++autoId));
28460             });
28461
28462         }
28463         
28464         var td = this.nextBlock();
28465         field.render(td);
28466         var ti = new Roo.Toolbar.Item(td.firstChild);
28467         ti.render(td);
28468         this.items.add(ti);
28469         this.fields.add(field);
28470         return ti;
28471     },
28472     /**
28473      * Hide the toolbar
28474      * @method hide
28475      */
28476      
28477       
28478     hide : function()
28479     {
28480         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28481         this.el.child('div').hide();
28482     },
28483     /**
28484      * Show the toolbar
28485      * @method show
28486      */
28487     show : function()
28488     {
28489         this.el.child('div').show();
28490     },
28491       
28492     // private
28493     nextBlock : function(){
28494         var td = document.createElement("td");
28495         this.tr.appendChild(td);
28496         return td;
28497     },
28498
28499     // private
28500     destroy : function(){
28501         if(this.items){ // rendered?
28502             Roo.destroy.apply(Roo, this.items.items);
28503         }
28504         if(this.fields){ // rendered?
28505             Roo.destroy.apply(Roo, this.fields.items);
28506         }
28507         Roo.Element.uncache(this.el, this.tr);
28508     }
28509 };
28510
28511 /**
28512  * @class Roo.Toolbar.Item
28513  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28514  * @constructor
28515  * Creates a new Item
28516  * @param {HTMLElement} el 
28517  */
28518 Roo.Toolbar.Item = function(el){
28519     this.el = Roo.getDom(el);
28520     this.id = Roo.id(this.el);
28521     this.hidden = false;
28522 };
28523
28524 Roo.Toolbar.Item.prototype = {
28525     
28526     /**
28527      * Get this item's HTML Element
28528      * @return {HTMLElement}
28529      */
28530     getEl : function(){
28531        return this.el;  
28532     },
28533
28534     // private
28535     render : function(td){
28536         this.td = td;
28537         td.appendChild(this.el);
28538     },
28539     
28540     /**
28541      * Removes and destroys this item.
28542      */
28543     destroy : function(){
28544         this.td.parentNode.removeChild(this.td);
28545     },
28546     
28547     /**
28548      * Shows this item.
28549      */
28550     show: function(){
28551         this.hidden = false;
28552         this.td.style.display = "";
28553     },
28554     
28555     /**
28556      * Hides this item.
28557      */
28558     hide: function(){
28559         this.hidden = true;
28560         this.td.style.display = "none";
28561     },
28562     
28563     /**
28564      * Convenience function for boolean show/hide.
28565      * @param {Boolean} visible true to show/false to hide
28566      */
28567     setVisible: function(visible){
28568         if(visible) {
28569             this.show();
28570         }else{
28571             this.hide();
28572         }
28573     },
28574     
28575     /**
28576      * Try to focus this item.
28577      */
28578     focus : function(){
28579         Roo.fly(this.el).focus();
28580     },
28581     
28582     /**
28583      * Disables this item.
28584      */
28585     disable : function(){
28586         Roo.fly(this.td).addClass("x-item-disabled");
28587         this.disabled = true;
28588         this.el.disabled = true;
28589     },
28590     
28591     /**
28592      * Enables this item.
28593      */
28594     enable : function(){
28595         Roo.fly(this.td).removeClass("x-item-disabled");
28596         this.disabled = false;
28597         this.el.disabled = false;
28598     }
28599 };
28600
28601
28602 /**
28603  * @class Roo.Toolbar.Separator
28604  * @extends Roo.Toolbar.Item
28605  * A simple toolbar separator class
28606  * @constructor
28607  * Creates a new Separator
28608  */
28609 Roo.Toolbar.Separator = function(){
28610     var s = document.createElement("span");
28611     s.className = "ytb-sep";
28612     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28613 };
28614 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28615     enable:Roo.emptyFn,
28616     disable:Roo.emptyFn,
28617     focus:Roo.emptyFn
28618 });
28619
28620 /**
28621  * @class Roo.Toolbar.Spacer
28622  * @extends Roo.Toolbar.Item
28623  * A simple element that adds extra horizontal space to a toolbar.
28624  * @constructor
28625  * Creates a new Spacer
28626  */
28627 Roo.Toolbar.Spacer = function(){
28628     var s = document.createElement("div");
28629     s.className = "ytb-spacer";
28630     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28631 };
28632 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28633     enable:Roo.emptyFn,
28634     disable:Roo.emptyFn,
28635     focus:Roo.emptyFn
28636 });
28637
28638 /**
28639  * @class Roo.Toolbar.Fill
28640  * @extends Roo.Toolbar.Spacer
28641  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28642  * @constructor
28643  * Creates a new Spacer
28644  */
28645 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28646     // private
28647     render : function(td){
28648         td.style.width = '100%';
28649         Roo.Toolbar.Fill.superclass.render.call(this, td);
28650     }
28651 });
28652
28653 /**
28654  * @class Roo.Toolbar.TextItem
28655  * @extends Roo.Toolbar.Item
28656  * A simple class that renders text directly into a toolbar.
28657  * @constructor
28658  * Creates a new TextItem
28659  * @param {String} text
28660  */
28661 Roo.Toolbar.TextItem = function(text){
28662     if (typeof(text) == 'object') {
28663         text = text.text;
28664     }
28665     var s = document.createElement("span");
28666     s.className = "ytb-text";
28667     s.innerHTML = text;
28668     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28669 };
28670 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28671     enable:Roo.emptyFn,
28672     disable:Roo.emptyFn,
28673     focus:Roo.emptyFn
28674 });
28675
28676 /**
28677  * @class Roo.Toolbar.Button
28678  * @extends Roo.Button
28679  * A button that renders into a toolbar.
28680  * @constructor
28681  * Creates a new Button
28682  * @param {Object} config A standard {@link Roo.Button} config object
28683  */
28684 Roo.Toolbar.Button = function(config){
28685     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28686 };
28687 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28688     render : function(td){
28689         this.td = td;
28690         Roo.Toolbar.Button.superclass.render.call(this, td);
28691     },
28692     
28693     /**
28694      * Removes and destroys this button
28695      */
28696     destroy : function(){
28697         Roo.Toolbar.Button.superclass.destroy.call(this);
28698         this.td.parentNode.removeChild(this.td);
28699     },
28700     
28701     /**
28702      * Shows this button
28703      */
28704     show: function(){
28705         this.hidden = false;
28706         this.td.style.display = "";
28707     },
28708     
28709     /**
28710      * Hides this button
28711      */
28712     hide: function(){
28713         this.hidden = true;
28714         this.td.style.display = "none";
28715     },
28716
28717     /**
28718      * Disables this item
28719      */
28720     disable : function(){
28721         Roo.fly(this.td).addClass("x-item-disabled");
28722         this.disabled = true;
28723     },
28724
28725     /**
28726      * Enables this item
28727      */
28728     enable : function(){
28729         Roo.fly(this.td).removeClass("x-item-disabled");
28730         this.disabled = false;
28731     }
28732 });
28733 // backwards compat
28734 Roo.ToolbarButton = Roo.Toolbar.Button;
28735
28736 /**
28737  * @class Roo.Toolbar.SplitButton
28738  * @extends Roo.SplitButton
28739  * A menu button that renders into a toolbar.
28740  * @constructor
28741  * Creates a new SplitButton
28742  * @param {Object} config A standard {@link Roo.SplitButton} config object
28743  */
28744 Roo.Toolbar.SplitButton = function(config){
28745     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28746 };
28747 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28748     render : function(td){
28749         this.td = td;
28750         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28751     },
28752     
28753     /**
28754      * Removes and destroys this button
28755      */
28756     destroy : function(){
28757         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28758         this.td.parentNode.removeChild(this.td);
28759     },
28760     
28761     /**
28762      * Shows this button
28763      */
28764     show: function(){
28765         this.hidden = false;
28766         this.td.style.display = "";
28767     },
28768     
28769     /**
28770      * Hides this button
28771      */
28772     hide: function(){
28773         this.hidden = true;
28774         this.td.style.display = "none";
28775     }
28776 });
28777
28778 // backwards compat
28779 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28780  * Based on:
28781  * Ext JS Library 1.1.1
28782  * Copyright(c) 2006-2007, Ext JS, LLC.
28783  *
28784  * Originally Released Under LGPL - original licence link has changed is not relivant.
28785  *
28786  * Fork - LGPL
28787  * <script type="text/javascript">
28788  */
28789  
28790 /**
28791  * @class Roo.PagingToolbar
28792  * @extends Roo.Toolbar
28793  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28794  * @constructor
28795  * Create a new PagingToolbar
28796  * @param {Object} config The config object
28797  */
28798 Roo.PagingToolbar = function(el, ds, config)
28799 {
28800     // old args format still supported... - xtype is prefered..
28801     if (typeof(el) == 'object' && el.xtype) {
28802         // created from xtype...
28803         config = el;
28804         ds = el.dataSource;
28805         el = config.container;
28806     }
28807     var items = [];
28808     if (config.items) {
28809         items = config.items;
28810         config.items = [];
28811     }
28812     
28813     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28814     this.ds = ds;
28815     this.cursor = 0;
28816     this.renderButtons(this.el);
28817     this.bind(ds);
28818     
28819     // supprot items array.
28820    
28821     Roo.each(items, function(e) {
28822         this.add(Roo.factory(e));
28823     },this);
28824     
28825 };
28826
28827 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28828     /**
28829      * @cfg {Roo.data.Store} dataSource
28830      * The underlying data store providing the paged data
28831      */
28832     /**
28833      * @cfg {String/HTMLElement/Element} container
28834      * container The id or element that will contain the toolbar
28835      */
28836     /**
28837      * @cfg {Boolean} displayInfo
28838      * True to display the displayMsg (defaults to false)
28839      */
28840     /**
28841      * @cfg {Number} pageSize
28842      * The number of records to display per page (defaults to 20)
28843      */
28844     pageSize: 20,
28845     /**
28846      * @cfg {String} displayMsg
28847      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28848      */
28849     displayMsg : 'Displaying {0} - {1} of {2}',
28850     /**
28851      * @cfg {String} emptyMsg
28852      * The message to display when no records are found (defaults to "No data to display")
28853      */
28854     emptyMsg : 'No data to display',
28855     /**
28856      * Customizable piece of the default paging text (defaults to "Page")
28857      * @type String
28858      */
28859     beforePageText : "Page",
28860     /**
28861      * Customizable piece of the default paging text (defaults to "of %0")
28862      * @type String
28863      */
28864     afterPageText : "of {0}",
28865     /**
28866      * Customizable piece of the default paging text (defaults to "First Page")
28867      * @type String
28868      */
28869     firstText : "First Page",
28870     /**
28871      * Customizable piece of the default paging text (defaults to "Previous Page")
28872      * @type String
28873      */
28874     prevText : "Previous Page",
28875     /**
28876      * Customizable piece of the default paging text (defaults to "Next Page")
28877      * @type String
28878      */
28879     nextText : "Next Page",
28880     /**
28881      * Customizable piece of the default paging text (defaults to "Last Page")
28882      * @type String
28883      */
28884     lastText : "Last Page",
28885     /**
28886      * Customizable piece of the default paging text (defaults to "Refresh")
28887      * @type String
28888      */
28889     refreshText : "Refresh",
28890
28891     // private
28892     renderButtons : function(el){
28893         Roo.PagingToolbar.superclass.render.call(this, el);
28894         this.first = this.addButton({
28895             tooltip: this.firstText,
28896             cls: "x-btn-icon x-grid-page-first",
28897             disabled: true,
28898             handler: this.onClick.createDelegate(this, ["first"])
28899         });
28900         this.prev = this.addButton({
28901             tooltip: this.prevText,
28902             cls: "x-btn-icon x-grid-page-prev",
28903             disabled: true,
28904             handler: this.onClick.createDelegate(this, ["prev"])
28905         });
28906         //this.addSeparator();
28907         this.add(this.beforePageText);
28908         this.field = Roo.get(this.addDom({
28909            tag: "input",
28910            type: "text",
28911            size: "3",
28912            value: "1",
28913            cls: "x-grid-page-number"
28914         }).el);
28915         this.field.on("keydown", this.onPagingKeydown, this);
28916         this.field.on("focus", function(){this.dom.select();});
28917         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28918         this.field.setHeight(18);
28919         //this.addSeparator();
28920         this.next = this.addButton({
28921             tooltip: this.nextText,
28922             cls: "x-btn-icon x-grid-page-next",
28923             disabled: true,
28924             handler: this.onClick.createDelegate(this, ["next"])
28925         });
28926         this.last = this.addButton({
28927             tooltip: this.lastText,
28928             cls: "x-btn-icon x-grid-page-last",
28929             disabled: true,
28930             handler: this.onClick.createDelegate(this, ["last"])
28931         });
28932         //this.addSeparator();
28933         this.loading = this.addButton({
28934             tooltip: this.refreshText,
28935             cls: "x-btn-icon x-grid-loading",
28936             handler: this.onClick.createDelegate(this, ["refresh"])
28937         });
28938
28939         if(this.displayInfo){
28940             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28941         }
28942     },
28943
28944     // private
28945     updateInfo : function(){
28946         if(this.displayEl){
28947             var count = this.ds.getCount();
28948             var msg = count == 0 ?
28949                 this.emptyMsg :
28950                 String.format(
28951                     this.displayMsg,
28952                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28953                 );
28954             this.displayEl.update(msg);
28955         }
28956     },
28957
28958     // private
28959     onLoad : function(ds, r, o){
28960        this.cursor = o.params ? o.params.start : 0;
28961        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28962
28963        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28964        this.field.dom.value = ap;
28965        this.first.setDisabled(ap == 1);
28966        this.prev.setDisabled(ap == 1);
28967        this.next.setDisabled(ap == ps);
28968        this.last.setDisabled(ap == ps);
28969        this.loading.enable();
28970        this.updateInfo();
28971     },
28972
28973     // private
28974     getPageData : function(){
28975         var total = this.ds.getTotalCount();
28976         return {
28977             total : total,
28978             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28979             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28980         };
28981     },
28982
28983     // private
28984     onLoadError : function(){
28985         this.loading.enable();
28986     },
28987
28988     // private
28989     onPagingKeydown : function(e){
28990         var k = e.getKey();
28991         var d = this.getPageData();
28992         if(k == e.RETURN){
28993             var v = this.field.dom.value, pageNum;
28994             if(!v || isNaN(pageNum = parseInt(v, 10))){
28995                 this.field.dom.value = d.activePage;
28996                 return;
28997             }
28998             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28999             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29000             e.stopEvent();
29001         }
29002         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))
29003         {
29004           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29005           this.field.dom.value = pageNum;
29006           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29007           e.stopEvent();
29008         }
29009         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29010         {
29011           var v = this.field.dom.value, pageNum; 
29012           var increment = (e.shiftKey) ? 10 : 1;
29013           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29014             increment *= -1;
29015           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29016             this.field.dom.value = d.activePage;
29017             return;
29018           }
29019           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29020           {
29021             this.field.dom.value = parseInt(v, 10) + increment;
29022             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29023             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29024           }
29025           e.stopEvent();
29026         }
29027     },
29028
29029     // private
29030     beforeLoad : function(){
29031         if(this.loading){
29032             this.loading.disable();
29033         }
29034     },
29035
29036     // private
29037     onClick : function(which){
29038         var ds = this.ds;
29039         switch(which){
29040             case "first":
29041                 ds.load({params:{start: 0, limit: this.pageSize}});
29042             break;
29043             case "prev":
29044                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29045             break;
29046             case "next":
29047                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29048             break;
29049             case "last":
29050                 var total = ds.getTotalCount();
29051                 var extra = total % this.pageSize;
29052                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29053                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29054             break;
29055             case "refresh":
29056                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29057             break;
29058         }
29059     },
29060
29061     /**
29062      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29063      * @param {Roo.data.Store} store The data store to unbind
29064      */
29065     unbind : function(ds){
29066         ds.un("beforeload", this.beforeLoad, this);
29067         ds.un("load", this.onLoad, this);
29068         ds.un("loadexception", this.onLoadError, this);
29069         ds.un("remove", this.updateInfo, this);
29070         ds.un("add", this.updateInfo, this);
29071         this.ds = undefined;
29072     },
29073
29074     /**
29075      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29076      * @param {Roo.data.Store} store The data store to bind
29077      */
29078     bind : function(ds){
29079         ds.on("beforeload", this.beforeLoad, this);
29080         ds.on("load", this.onLoad, this);
29081         ds.on("loadexception", this.onLoadError, this);
29082         ds.on("remove", this.updateInfo, this);
29083         ds.on("add", this.updateInfo, this);
29084         this.ds = ds;
29085     }
29086 });/*
29087  * Based on:
29088  * Ext JS Library 1.1.1
29089  * Copyright(c) 2006-2007, Ext JS, LLC.
29090  *
29091  * Originally Released Under LGPL - original licence link has changed is not relivant.
29092  *
29093  * Fork - LGPL
29094  * <script type="text/javascript">
29095  */
29096
29097 /**
29098  * @class Roo.Resizable
29099  * @extends Roo.util.Observable
29100  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29101  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29102  * 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
29103  * the element will be wrapped for you automatically.</p>
29104  * <p>Here is the list of valid resize handles:</p>
29105  * <pre>
29106 Value   Description
29107 ------  -------------------
29108  'n'     north
29109  's'     south
29110  'e'     east
29111  'w'     west
29112  'nw'    northwest
29113  'sw'    southwest
29114  'se'    southeast
29115  'ne'    northeast
29116  'hd'    horizontal drag
29117  'all'   all
29118 </pre>
29119  * <p>Here's an example showing the creation of a typical Resizable:</p>
29120  * <pre><code>
29121 var resizer = new Roo.Resizable("element-id", {
29122     handles: 'all',
29123     minWidth: 200,
29124     minHeight: 100,
29125     maxWidth: 500,
29126     maxHeight: 400,
29127     pinned: true
29128 });
29129 resizer.on("resize", myHandler);
29130 </code></pre>
29131  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29132  * resizer.east.setDisplayed(false);</p>
29133  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29134  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29135  * resize operation's new size (defaults to [0, 0])
29136  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29137  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29138  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29139  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29140  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29141  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29142  * @cfg {Number} width The width of the element in pixels (defaults to null)
29143  * @cfg {Number} height The height of the element in pixels (defaults to null)
29144  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29145  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29146  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29147  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29148  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29149  * in favor of the handles config option (defaults to false)
29150  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29151  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29152  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29153  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29154  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29155  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29156  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29157  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29158  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29159  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29160  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29161  * @constructor
29162  * Create a new resizable component
29163  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29164  * @param {Object} config configuration options
29165   */
29166 Roo.Resizable = function(el, config)
29167 {
29168     this.el = Roo.get(el);
29169
29170     if(config && config.wrap){
29171         config.resizeChild = this.el;
29172         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29173         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29174         this.el.setStyle("overflow", "hidden");
29175         this.el.setPositioning(config.resizeChild.getPositioning());
29176         config.resizeChild.clearPositioning();
29177         if(!config.width || !config.height){
29178             var csize = config.resizeChild.getSize();
29179             this.el.setSize(csize.width, csize.height);
29180         }
29181         if(config.pinned && !config.adjustments){
29182             config.adjustments = "auto";
29183         }
29184     }
29185
29186     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29187     this.proxy.unselectable();
29188     this.proxy.enableDisplayMode('block');
29189
29190     Roo.apply(this, config);
29191
29192     if(this.pinned){
29193         this.disableTrackOver = true;
29194         this.el.addClass("x-resizable-pinned");
29195     }
29196     // if the element isn't positioned, make it relative
29197     var position = this.el.getStyle("position");
29198     if(position != "absolute" && position != "fixed"){
29199         this.el.setStyle("position", "relative");
29200     }
29201     if(!this.handles){ // no handles passed, must be legacy style
29202         this.handles = 's,e,se';
29203         if(this.multiDirectional){
29204             this.handles += ',n,w';
29205         }
29206     }
29207     if(this.handles == "all"){
29208         this.handles = "n s e w ne nw se sw";
29209     }
29210     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29211     var ps = Roo.Resizable.positions;
29212     for(var i = 0, len = hs.length; i < len; i++){
29213         if(hs[i] && ps[hs[i]]){
29214             var pos = ps[hs[i]];
29215             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29216         }
29217     }
29218     // legacy
29219     this.corner = this.southeast;
29220     
29221     // updateBox = the box can move..
29222     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29223         this.updateBox = true;
29224     }
29225
29226     this.activeHandle = null;
29227
29228     if(this.resizeChild){
29229         if(typeof this.resizeChild == "boolean"){
29230             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29231         }else{
29232             this.resizeChild = Roo.get(this.resizeChild, true);
29233         }
29234     }
29235     
29236     if(this.adjustments == "auto"){
29237         var rc = this.resizeChild;
29238         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29239         if(rc && (hw || hn)){
29240             rc.position("relative");
29241             rc.setLeft(hw ? hw.el.getWidth() : 0);
29242             rc.setTop(hn ? hn.el.getHeight() : 0);
29243         }
29244         this.adjustments = [
29245             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29246             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29247         ];
29248     }
29249
29250     if(this.draggable){
29251         this.dd = this.dynamic ?
29252             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29253         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29254     }
29255
29256     // public events
29257     this.addEvents({
29258         /**
29259          * @event beforeresize
29260          * Fired before resize is allowed. Set enabled to false to cancel resize.
29261          * @param {Roo.Resizable} this
29262          * @param {Roo.EventObject} e The mousedown event
29263          */
29264         "beforeresize" : true,
29265         /**
29266          * @event resizing
29267          * Fired a resizing.
29268          * @param {Roo.Resizable} this
29269          * @param {Number} x The new x position
29270          * @param {Number} y The new y position
29271          * @param {Number} w The new w width
29272          * @param {Number} h The new h hight
29273          * @param {Roo.EventObject} e The mouseup event
29274          */
29275         "resizing" : true,
29276         /**
29277          * @event resize
29278          * Fired after a resize.
29279          * @param {Roo.Resizable} this
29280          * @param {Number} width The new width
29281          * @param {Number} height The new height
29282          * @param {Roo.EventObject} e The mouseup event
29283          */
29284         "resize" : true
29285     });
29286
29287     if(this.width !== null && this.height !== null){
29288         this.resizeTo(this.width, this.height);
29289     }else{
29290         this.updateChildSize();
29291     }
29292     if(Roo.isIE){
29293         this.el.dom.style.zoom = 1;
29294     }
29295     Roo.Resizable.superclass.constructor.call(this);
29296 };
29297
29298 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29299         resizeChild : false,
29300         adjustments : [0, 0],
29301         minWidth : 5,
29302         minHeight : 5,
29303         maxWidth : 10000,
29304         maxHeight : 10000,
29305         enabled : true,
29306         animate : false,
29307         duration : .35,
29308         dynamic : false,
29309         handles : false,
29310         multiDirectional : false,
29311         disableTrackOver : false,
29312         easing : 'easeOutStrong',
29313         widthIncrement : 0,
29314         heightIncrement : 0,
29315         pinned : false,
29316         width : null,
29317         height : null,
29318         preserveRatio : false,
29319         transparent: false,
29320         minX: 0,
29321         minY: 0,
29322         draggable: false,
29323
29324         /**
29325          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29326          */
29327         constrainTo: undefined,
29328         /**
29329          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29330          */
29331         resizeRegion: undefined,
29332
29333
29334     /**
29335      * Perform a manual resize
29336      * @param {Number} width
29337      * @param {Number} height
29338      */
29339     resizeTo : function(width, height){
29340         this.el.setSize(width, height);
29341         this.updateChildSize();
29342         this.fireEvent("resize", this, width, height, null);
29343     },
29344
29345     // private
29346     startSizing : function(e, handle){
29347         this.fireEvent("beforeresize", this, e);
29348         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29349
29350             if(!this.overlay){
29351                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29352                 this.overlay.unselectable();
29353                 this.overlay.enableDisplayMode("block");
29354                 this.overlay.on("mousemove", this.onMouseMove, this);
29355                 this.overlay.on("mouseup", this.onMouseUp, this);
29356             }
29357             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29358
29359             this.resizing = true;
29360             this.startBox = this.el.getBox();
29361             this.startPoint = e.getXY();
29362             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29363                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29364
29365             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29366             this.overlay.show();
29367
29368             if(this.constrainTo) {
29369                 var ct = Roo.get(this.constrainTo);
29370                 this.resizeRegion = ct.getRegion().adjust(
29371                     ct.getFrameWidth('t'),
29372                     ct.getFrameWidth('l'),
29373                     -ct.getFrameWidth('b'),
29374                     -ct.getFrameWidth('r')
29375                 );
29376             }
29377
29378             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29379             this.proxy.show();
29380             this.proxy.setBox(this.startBox);
29381             if(!this.dynamic){
29382                 this.proxy.setStyle('visibility', 'visible');
29383             }
29384         }
29385     },
29386
29387     // private
29388     onMouseDown : function(handle, e){
29389         if(this.enabled){
29390             e.stopEvent();
29391             this.activeHandle = handle;
29392             this.startSizing(e, handle);
29393         }
29394     },
29395
29396     // private
29397     onMouseUp : function(e){
29398         var size = this.resizeElement();
29399         this.resizing = false;
29400         this.handleOut();
29401         this.overlay.hide();
29402         this.proxy.hide();
29403         this.fireEvent("resize", this, size.width, size.height, e);
29404     },
29405
29406     // private
29407     updateChildSize : function(){
29408         
29409         if(this.resizeChild){
29410             var el = this.el;
29411             var child = this.resizeChild;
29412             var adj = this.adjustments;
29413             if(el.dom.offsetWidth){
29414                 var b = el.getSize(true);
29415                 child.setSize(b.width+adj[0], b.height+adj[1]);
29416             }
29417             // Second call here for IE
29418             // The first call enables instant resizing and
29419             // the second call corrects scroll bars if they
29420             // exist
29421             if(Roo.isIE){
29422                 setTimeout(function(){
29423                     if(el.dom.offsetWidth){
29424                         var b = el.getSize(true);
29425                         child.setSize(b.width+adj[0], b.height+adj[1]);
29426                     }
29427                 }, 10);
29428             }
29429         }
29430     },
29431
29432     // private
29433     snap : function(value, inc, min){
29434         if(!inc || !value) return value;
29435         var newValue = value;
29436         var m = value % inc;
29437         if(m > 0){
29438             if(m > (inc/2)){
29439                 newValue = value + (inc-m);
29440             }else{
29441                 newValue = value - m;
29442             }
29443         }
29444         return Math.max(min, newValue);
29445     },
29446
29447     // private
29448     resizeElement : function(){
29449         var box = this.proxy.getBox();
29450         if(this.updateBox){
29451             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29452         }else{
29453             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29454         }
29455         this.updateChildSize();
29456         if(!this.dynamic){
29457             this.proxy.hide();
29458         }
29459         return box;
29460     },
29461
29462     // private
29463     constrain : function(v, diff, m, mx){
29464         if(v - diff < m){
29465             diff = v - m;
29466         }else if(v - diff > mx){
29467             diff = mx - v;
29468         }
29469         return diff;
29470     },
29471
29472     // private
29473     onMouseMove : function(e){
29474         
29475         if(this.enabled){
29476             try{// try catch so if something goes wrong the user doesn't get hung
29477
29478             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29479                 return;
29480             }
29481
29482             //var curXY = this.startPoint;
29483             var curSize = this.curSize || this.startBox;
29484             var x = this.startBox.x, y = this.startBox.y;
29485             var ox = x, oy = y;
29486             var w = curSize.width, h = curSize.height;
29487             var ow = w, oh = h;
29488             var mw = this.minWidth, mh = this.minHeight;
29489             var mxw = this.maxWidth, mxh = this.maxHeight;
29490             var wi = this.widthIncrement;
29491             var hi = this.heightIncrement;
29492
29493             var eventXY = e.getXY();
29494             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29495             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29496
29497             var pos = this.activeHandle.position;
29498
29499             switch(pos){
29500                 case "east":
29501                     w += diffX;
29502                     w = Math.min(Math.max(mw, w), mxw);
29503                     break;
29504              
29505                 case "south":
29506                     h += diffY;
29507                     h = Math.min(Math.max(mh, h), mxh);
29508                     break;
29509                 case "southeast":
29510                     w += diffX;
29511                     h += diffY;
29512                     w = Math.min(Math.max(mw, w), mxw);
29513                     h = Math.min(Math.max(mh, h), mxh);
29514                     break;
29515                 case "north":
29516                     diffY = this.constrain(h, diffY, mh, mxh);
29517                     y += diffY;
29518                     h -= diffY;
29519                     break;
29520                 case "hdrag":
29521                     
29522                     if (wi) {
29523                         var adiffX = Math.abs(diffX);
29524                         var sub = (adiffX % wi); // how much 
29525                         if (sub > (wi/2)) { // far enough to snap
29526                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29527                         } else {
29528                             // remove difference.. 
29529                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29530                         }
29531                     }
29532                     x += diffX;
29533                     x = Math.max(this.minX, x);
29534                     break;
29535                 case "west":
29536                     diffX = this.constrain(w, diffX, mw, mxw);
29537                     x += diffX;
29538                     w -= diffX;
29539                     break;
29540                 case "northeast":
29541                     w += diffX;
29542                     w = Math.min(Math.max(mw, w), mxw);
29543                     diffY = this.constrain(h, diffY, mh, mxh);
29544                     y += diffY;
29545                     h -= diffY;
29546                     break;
29547                 case "northwest":
29548                     diffX = this.constrain(w, diffX, mw, mxw);
29549                     diffY = this.constrain(h, diffY, mh, mxh);
29550                     y += diffY;
29551                     h -= diffY;
29552                     x += diffX;
29553                     w -= diffX;
29554                     break;
29555                case "southwest":
29556                     diffX = this.constrain(w, diffX, mw, mxw);
29557                     h += diffY;
29558                     h = Math.min(Math.max(mh, h), mxh);
29559                     x += diffX;
29560                     w -= diffX;
29561                     break;
29562             }
29563
29564             var sw = this.snap(w, wi, mw);
29565             var sh = this.snap(h, hi, mh);
29566             if(sw != w || sh != h){
29567                 switch(pos){
29568                     case "northeast":
29569                         y -= sh - h;
29570                     break;
29571                     case "north":
29572                         y -= sh - h;
29573                         break;
29574                     case "southwest":
29575                         x -= sw - w;
29576                     break;
29577                     case "west":
29578                         x -= sw - w;
29579                         break;
29580                     case "northwest":
29581                         x -= sw - w;
29582                         y -= sh - h;
29583                     break;
29584                 }
29585                 w = sw;
29586                 h = sh;
29587             }
29588
29589             if(this.preserveRatio){
29590                 switch(pos){
29591                     case "southeast":
29592                     case "east":
29593                         h = oh * (w/ow);
29594                         h = Math.min(Math.max(mh, h), mxh);
29595                         w = ow * (h/oh);
29596                        break;
29597                     case "south":
29598                         w = ow * (h/oh);
29599                         w = Math.min(Math.max(mw, w), mxw);
29600                         h = oh * (w/ow);
29601                         break;
29602                     case "northeast":
29603                         w = ow * (h/oh);
29604                         w = Math.min(Math.max(mw, w), mxw);
29605                         h = oh * (w/ow);
29606                     break;
29607                     case "north":
29608                         var tw = w;
29609                         w = ow * (h/oh);
29610                         w = Math.min(Math.max(mw, w), mxw);
29611                         h = oh * (w/ow);
29612                         x += (tw - w) / 2;
29613                         break;
29614                     case "southwest":
29615                         h = oh * (w/ow);
29616                         h = Math.min(Math.max(mh, h), mxh);
29617                         var tw = w;
29618                         w = ow * (h/oh);
29619                         x += tw - w;
29620                         break;
29621                     case "west":
29622                         var th = h;
29623                         h = oh * (w/ow);
29624                         h = Math.min(Math.max(mh, h), mxh);
29625                         y += (th - h) / 2;
29626                         var tw = w;
29627                         w = ow * (h/oh);
29628                         x += tw - w;
29629                        break;
29630                     case "northwest":
29631                         var tw = w;
29632                         var th = h;
29633                         h = oh * (w/ow);
29634                         h = Math.min(Math.max(mh, h), mxh);
29635                         w = ow * (h/oh);
29636                         y += th - h;
29637                         x += tw - w;
29638                        break;
29639
29640                 }
29641             }
29642             if (pos == 'hdrag') {
29643                 w = ow;
29644             }
29645             this.proxy.setBounds(x, y, w, h);
29646             if(this.dynamic){
29647                 this.resizeElement();
29648             }
29649             }catch(e){}
29650         }
29651         this.fireEvent("resizing", this, x, y, w, h, e);
29652     },
29653
29654     // private
29655     handleOver : function(){
29656         if(this.enabled){
29657             this.el.addClass("x-resizable-over");
29658         }
29659     },
29660
29661     // private
29662     handleOut : function(){
29663         if(!this.resizing){
29664             this.el.removeClass("x-resizable-over");
29665         }
29666     },
29667
29668     /**
29669      * Returns the element this component is bound to.
29670      * @return {Roo.Element}
29671      */
29672     getEl : function(){
29673         return this.el;
29674     },
29675
29676     /**
29677      * Returns the resizeChild element (or null).
29678      * @return {Roo.Element}
29679      */
29680     getResizeChild : function(){
29681         return this.resizeChild;
29682     },
29683     groupHandler : function()
29684     {
29685         
29686     },
29687     /**
29688      * Destroys this resizable. If the element was wrapped and
29689      * removeEl is not true then the element remains.
29690      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29691      */
29692     destroy : function(removeEl){
29693         this.proxy.remove();
29694         if(this.overlay){
29695             this.overlay.removeAllListeners();
29696             this.overlay.remove();
29697         }
29698         var ps = Roo.Resizable.positions;
29699         for(var k in ps){
29700             if(typeof ps[k] != "function" && this[ps[k]]){
29701                 var h = this[ps[k]];
29702                 h.el.removeAllListeners();
29703                 h.el.remove();
29704             }
29705         }
29706         if(removeEl){
29707             this.el.update("");
29708             this.el.remove();
29709         }
29710     }
29711 });
29712
29713 // private
29714 // hash to map config positions to true positions
29715 Roo.Resizable.positions = {
29716     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29717     hd: "hdrag"
29718 };
29719
29720 // private
29721 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29722     if(!this.tpl){
29723         // only initialize the template if resizable is used
29724         var tpl = Roo.DomHelper.createTemplate(
29725             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29726         );
29727         tpl.compile();
29728         Roo.Resizable.Handle.prototype.tpl = tpl;
29729     }
29730     this.position = pos;
29731     this.rz = rz;
29732     // show north drag fro topdra
29733     var handlepos = pos == 'hdrag' ? 'north' : pos;
29734     
29735     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29736     if (pos == 'hdrag') {
29737         this.el.setStyle('cursor', 'pointer');
29738     }
29739     this.el.unselectable();
29740     if(transparent){
29741         this.el.setOpacity(0);
29742     }
29743     this.el.on("mousedown", this.onMouseDown, this);
29744     if(!disableTrackOver){
29745         this.el.on("mouseover", this.onMouseOver, this);
29746         this.el.on("mouseout", this.onMouseOut, this);
29747     }
29748 };
29749
29750 // private
29751 Roo.Resizable.Handle.prototype = {
29752     afterResize : function(rz){
29753         Roo.log('after?');
29754         // do nothing
29755     },
29756     // private
29757     onMouseDown : function(e){
29758         this.rz.onMouseDown(this, e);
29759     },
29760     // private
29761     onMouseOver : function(e){
29762         this.rz.handleOver(this, e);
29763     },
29764     // private
29765     onMouseOut : function(e){
29766         this.rz.handleOut(this, e);
29767     }
29768 };/*
29769  * Based on:
29770  * Ext JS Library 1.1.1
29771  * Copyright(c) 2006-2007, Ext JS, LLC.
29772  *
29773  * Originally Released Under LGPL - original licence link has changed is not relivant.
29774  *
29775  * Fork - LGPL
29776  * <script type="text/javascript">
29777  */
29778
29779 /**
29780  * @class Roo.Editor
29781  * @extends Roo.Component
29782  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29783  * @constructor
29784  * Create a new Editor
29785  * @param {Roo.form.Field} field The Field object (or descendant)
29786  * @param {Object} config The config object
29787  */
29788 Roo.Editor = function(field, config){
29789     Roo.Editor.superclass.constructor.call(this, config);
29790     this.field = field;
29791     this.addEvents({
29792         /**
29793              * @event beforestartedit
29794              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29795              * false from the handler of this event.
29796              * @param {Editor} this
29797              * @param {Roo.Element} boundEl The underlying element bound to this editor
29798              * @param {Mixed} value The field value being set
29799              */
29800         "beforestartedit" : true,
29801         /**
29802              * @event startedit
29803              * Fires when this editor is displayed
29804              * @param {Roo.Element} boundEl The underlying element bound to this editor
29805              * @param {Mixed} value The starting field value
29806              */
29807         "startedit" : true,
29808         /**
29809              * @event beforecomplete
29810              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29811              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29812              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29813              * event will not fire since no edit actually occurred.
29814              * @param {Editor} this
29815              * @param {Mixed} value The current field value
29816              * @param {Mixed} startValue The original field value
29817              */
29818         "beforecomplete" : true,
29819         /**
29820              * @event complete
29821              * Fires after editing is complete and any changed value has been written to the underlying field.
29822              * @param {Editor} this
29823              * @param {Mixed} value The current field value
29824              * @param {Mixed} startValue The original field value
29825              */
29826         "complete" : true,
29827         /**
29828          * @event specialkey
29829          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29830          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29831          * @param {Roo.form.Field} this
29832          * @param {Roo.EventObject} e The event object
29833          */
29834         "specialkey" : true
29835     });
29836 };
29837
29838 Roo.extend(Roo.Editor, Roo.Component, {
29839     /**
29840      * @cfg {Boolean/String} autosize
29841      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29842      * or "height" to adopt the height only (defaults to false)
29843      */
29844     /**
29845      * @cfg {Boolean} revertInvalid
29846      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29847      * validation fails (defaults to true)
29848      */
29849     /**
29850      * @cfg {Boolean} ignoreNoChange
29851      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29852      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29853      * will never be ignored.
29854      */
29855     /**
29856      * @cfg {Boolean} hideEl
29857      * False to keep the bound element visible while the editor is displayed (defaults to true)
29858      */
29859     /**
29860      * @cfg {Mixed} value
29861      * The data value of the underlying field (defaults to "")
29862      */
29863     value : "",
29864     /**
29865      * @cfg {String} alignment
29866      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29867      */
29868     alignment: "c-c?",
29869     /**
29870      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29871      * for bottom-right shadow (defaults to "frame")
29872      */
29873     shadow : "frame",
29874     /**
29875      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29876      */
29877     constrain : false,
29878     /**
29879      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29880      */
29881     completeOnEnter : false,
29882     /**
29883      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29884      */
29885     cancelOnEsc : false,
29886     /**
29887      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29888      */
29889     updateEl : false,
29890
29891     // private
29892     onRender : function(ct, position){
29893         this.el = new Roo.Layer({
29894             shadow: this.shadow,
29895             cls: "x-editor",
29896             parentEl : ct,
29897             shim : this.shim,
29898             shadowOffset:4,
29899             id: this.id,
29900             constrain: this.constrain
29901         });
29902         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29903         if(this.field.msgTarget != 'title'){
29904             this.field.msgTarget = 'qtip';
29905         }
29906         this.field.render(this.el);
29907         if(Roo.isGecko){
29908             this.field.el.dom.setAttribute('autocomplete', 'off');
29909         }
29910         this.field.on("specialkey", this.onSpecialKey, this);
29911         if(this.swallowKeys){
29912             this.field.el.swallowEvent(['keydown','keypress']);
29913         }
29914         this.field.show();
29915         this.field.on("blur", this.onBlur, this);
29916         if(this.field.grow){
29917             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29918         }
29919     },
29920
29921     onSpecialKey : function(field, e)
29922     {
29923         //Roo.log('editor onSpecialKey');
29924         if(this.completeOnEnter && e.getKey() == e.ENTER){
29925             e.stopEvent();
29926             this.completeEdit();
29927             return;
29928         }
29929         // do not fire special key otherwise it might hide close the editor...
29930         if(e.getKey() == e.ENTER){    
29931             return;
29932         }
29933         if(this.cancelOnEsc && e.getKey() == e.ESC){
29934             this.cancelEdit();
29935             return;
29936         } 
29937         this.fireEvent('specialkey', field, e);
29938     
29939     },
29940
29941     /**
29942      * Starts the editing process and shows the editor.
29943      * @param {String/HTMLElement/Element} el The element to edit
29944      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29945       * to the innerHTML of el.
29946      */
29947     startEdit : function(el, value){
29948         if(this.editing){
29949             this.completeEdit();
29950         }
29951         this.boundEl = Roo.get(el);
29952         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29953         if(!this.rendered){
29954             this.render(this.parentEl || document.body);
29955         }
29956         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29957             return;
29958         }
29959         this.startValue = v;
29960         this.field.setValue(v);
29961         if(this.autoSize){
29962             var sz = this.boundEl.getSize();
29963             switch(this.autoSize){
29964                 case "width":
29965                 this.setSize(sz.width,  "");
29966                 break;
29967                 case "height":
29968                 this.setSize("",  sz.height);
29969                 break;
29970                 default:
29971                 this.setSize(sz.width,  sz.height);
29972             }
29973         }
29974         this.el.alignTo(this.boundEl, this.alignment);
29975         this.editing = true;
29976         if(Roo.QuickTips){
29977             Roo.QuickTips.disable();
29978         }
29979         this.show();
29980     },
29981
29982     /**
29983      * Sets the height and width of this editor.
29984      * @param {Number} width The new width
29985      * @param {Number} height The new height
29986      */
29987     setSize : function(w, h){
29988         this.field.setSize(w, h);
29989         if(this.el){
29990             this.el.sync();
29991         }
29992     },
29993
29994     /**
29995      * Realigns the editor to the bound field based on the current alignment config value.
29996      */
29997     realign : function(){
29998         this.el.alignTo(this.boundEl, this.alignment);
29999     },
30000
30001     /**
30002      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30003      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30004      */
30005     completeEdit : function(remainVisible){
30006         if(!this.editing){
30007             return;
30008         }
30009         var v = this.getValue();
30010         if(this.revertInvalid !== false && !this.field.isValid()){
30011             v = this.startValue;
30012             this.cancelEdit(true);
30013         }
30014         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30015             this.editing = false;
30016             this.hide();
30017             return;
30018         }
30019         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30020             this.editing = false;
30021             if(this.updateEl && this.boundEl){
30022                 this.boundEl.update(v);
30023             }
30024             if(remainVisible !== true){
30025                 this.hide();
30026             }
30027             this.fireEvent("complete", this, v, this.startValue);
30028         }
30029     },
30030
30031     // private
30032     onShow : function(){
30033         this.el.show();
30034         if(this.hideEl !== false){
30035             this.boundEl.hide();
30036         }
30037         this.field.show();
30038         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30039             this.fixIEFocus = true;
30040             this.deferredFocus.defer(50, this);
30041         }else{
30042             this.field.focus();
30043         }
30044         this.fireEvent("startedit", this.boundEl, this.startValue);
30045     },
30046
30047     deferredFocus : function(){
30048         if(this.editing){
30049             this.field.focus();
30050         }
30051     },
30052
30053     /**
30054      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30055      * reverted to the original starting value.
30056      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30057      * cancel (defaults to false)
30058      */
30059     cancelEdit : function(remainVisible){
30060         if(this.editing){
30061             this.setValue(this.startValue);
30062             if(remainVisible !== true){
30063                 this.hide();
30064             }
30065         }
30066     },
30067
30068     // private
30069     onBlur : function(){
30070         if(this.allowBlur !== true && this.editing){
30071             this.completeEdit();
30072         }
30073     },
30074
30075     // private
30076     onHide : function(){
30077         if(this.editing){
30078             this.completeEdit();
30079             return;
30080         }
30081         this.field.blur();
30082         if(this.field.collapse){
30083             this.field.collapse();
30084         }
30085         this.el.hide();
30086         if(this.hideEl !== false){
30087             this.boundEl.show();
30088         }
30089         if(Roo.QuickTips){
30090             Roo.QuickTips.enable();
30091         }
30092     },
30093
30094     /**
30095      * Sets the data value of the editor
30096      * @param {Mixed} value Any valid value supported by the underlying field
30097      */
30098     setValue : function(v){
30099         this.field.setValue(v);
30100     },
30101
30102     /**
30103      * Gets the data value of the editor
30104      * @return {Mixed} The data value
30105      */
30106     getValue : function(){
30107         return this.field.getValue();
30108     }
30109 });/*
30110  * Based on:
30111  * Ext JS Library 1.1.1
30112  * Copyright(c) 2006-2007, Ext JS, LLC.
30113  *
30114  * Originally Released Under LGPL - original licence link has changed is not relivant.
30115  *
30116  * Fork - LGPL
30117  * <script type="text/javascript">
30118  */
30119  
30120 /**
30121  * @class Roo.BasicDialog
30122  * @extends Roo.util.Observable
30123  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30124  * <pre><code>
30125 var dlg = new Roo.BasicDialog("my-dlg", {
30126     height: 200,
30127     width: 300,
30128     minHeight: 100,
30129     minWidth: 150,
30130     modal: true,
30131     proxyDrag: true,
30132     shadow: true
30133 });
30134 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30135 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30136 dlg.addButton('Cancel', dlg.hide, dlg);
30137 dlg.show();
30138 </code></pre>
30139   <b>A Dialog should always be a direct child of the body element.</b>
30140  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30141  * @cfg {String} title Default text to display in the title bar (defaults to null)
30142  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30143  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30144  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30145  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30146  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30147  * (defaults to null with no animation)
30148  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30149  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30150  * property for valid values (defaults to 'all')
30151  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30152  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30153  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30154  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30155  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30156  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30157  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30158  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30159  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30160  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30161  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30162  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30163  * draggable = true (defaults to false)
30164  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30165  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30166  * shadow (defaults to false)
30167  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30168  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30169  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30170  * @cfg {Array} buttons Array of buttons
30171  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30172  * @constructor
30173  * Create a new BasicDialog.
30174  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30175  * @param {Object} config Configuration options
30176  */
30177 Roo.BasicDialog = function(el, config){
30178     this.el = Roo.get(el);
30179     var dh = Roo.DomHelper;
30180     if(!this.el && config && config.autoCreate){
30181         if(typeof config.autoCreate == "object"){
30182             if(!config.autoCreate.id){
30183                 config.autoCreate.id = el;
30184             }
30185             this.el = dh.append(document.body,
30186                         config.autoCreate, true);
30187         }else{
30188             this.el = dh.append(document.body,
30189                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30190         }
30191     }
30192     el = this.el;
30193     el.setDisplayed(true);
30194     el.hide = this.hideAction;
30195     this.id = el.id;
30196     el.addClass("x-dlg");
30197
30198     Roo.apply(this, config);
30199
30200     this.proxy = el.createProxy("x-dlg-proxy");
30201     this.proxy.hide = this.hideAction;
30202     this.proxy.setOpacity(.5);
30203     this.proxy.hide();
30204
30205     if(config.width){
30206         el.setWidth(config.width);
30207     }
30208     if(config.height){
30209         el.setHeight(config.height);
30210     }
30211     this.size = el.getSize();
30212     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30213         this.xy = [config.x,config.y];
30214     }else{
30215         this.xy = el.getCenterXY(true);
30216     }
30217     /** The header element @type Roo.Element */
30218     this.header = el.child("> .x-dlg-hd");
30219     /** The body element @type Roo.Element */
30220     this.body = el.child("> .x-dlg-bd");
30221     /** The footer element @type Roo.Element */
30222     this.footer = el.child("> .x-dlg-ft");
30223
30224     if(!this.header){
30225         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30226     }
30227     if(!this.body){
30228         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30229     }
30230
30231     this.header.unselectable();
30232     if(this.title){
30233         this.header.update(this.title);
30234     }
30235     // this element allows the dialog to be focused for keyboard event
30236     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30237     this.focusEl.swallowEvent("click", true);
30238
30239     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30240
30241     // wrap the body and footer for special rendering
30242     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30243     if(this.footer){
30244         this.bwrap.dom.appendChild(this.footer.dom);
30245     }
30246
30247     this.bg = this.el.createChild({
30248         tag: "div", cls:"x-dlg-bg",
30249         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30250     });
30251     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30252
30253
30254     if(this.autoScroll !== false && !this.autoTabs){
30255         this.body.setStyle("overflow", "auto");
30256     }
30257
30258     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30259
30260     if(this.closable !== false){
30261         this.el.addClass("x-dlg-closable");
30262         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30263         this.close.on("click", this.closeClick, this);
30264         this.close.addClassOnOver("x-dlg-close-over");
30265     }
30266     if(this.collapsible !== false){
30267         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30268         this.collapseBtn.on("click", this.collapseClick, this);
30269         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30270         this.header.on("dblclick", this.collapseClick, this);
30271     }
30272     if(this.resizable !== false){
30273         this.el.addClass("x-dlg-resizable");
30274         this.resizer = new Roo.Resizable(el, {
30275             minWidth: this.minWidth || 80,
30276             minHeight:this.minHeight || 80,
30277             handles: this.resizeHandles || "all",
30278             pinned: true
30279         });
30280         this.resizer.on("beforeresize", this.beforeResize, this);
30281         this.resizer.on("resize", this.onResize, this);
30282     }
30283     if(this.draggable !== false){
30284         el.addClass("x-dlg-draggable");
30285         if (!this.proxyDrag) {
30286             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30287         }
30288         else {
30289             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30290         }
30291         dd.setHandleElId(this.header.id);
30292         dd.endDrag = this.endMove.createDelegate(this);
30293         dd.startDrag = this.startMove.createDelegate(this);
30294         dd.onDrag = this.onDrag.createDelegate(this);
30295         dd.scroll = false;
30296         this.dd = dd;
30297     }
30298     if(this.modal){
30299         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30300         this.mask.enableDisplayMode("block");
30301         this.mask.hide();
30302         this.el.addClass("x-dlg-modal");
30303     }
30304     if(this.shadow){
30305         this.shadow = new Roo.Shadow({
30306             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30307             offset : this.shadowOffset
30308         });
30309     }else{
30310         this.shadowOffset = 0;
30311     }
30312     if(Roo.useShims && this.shim !== false){
30313         this.shim = this.el.createShim();
30314         this.shim.hide = this.hideAction;
30315         this.shim.hide();
30316     }else{
30317         this.shim = false;
30318     }
30319     if(this.autoTabs){
30320         this.initTabs();
30321     }
30322     if (this.buttons) { 
30323         var bts= this.buttons;
30324         this.buttons = [];
30325         Roo.each(bts, function(b) {
30326             this.addButton(b);
30327         }, this);
30328     }
30329     
30330     
30331     this.addEvents({
30332         /**
30333          * @event keydown
30334          * Fires when a key is pressed
30335          * @param {Roo.BasicDialog} this
30336          * @param {Roo.EventObject} e
30337          */
30338         "keydown" : true,
30339         /**
30340          * @event move
30341          * Fires when this dialog is moved by the user.
30342          * @param {Roo.BasicDialog} this
30343          * @param {Number} x The new page X
30344          * @param {Number} y The new page Y
30345          */
30346         "move" : true,
30347         /**
30348          * @event resize
30349          * Fires when this dialog is resized by the user.
30350          * @param {Roo.BasicDialog} this
30351          * @param {Number} width The new width
30352          * @param {Number} height The new height
30353          */
30354         "resize" : true,
30355         /**
30356          * @event beforehide
30357          * Fires before this dialog is hidden.
30358          * @param {Roo.BasicDialog} this
30359          */
30360         "beforehide" : true,
30361         /**
30362          * @event hide
30363          * Fires when this dialog is hidden.
30364          * @param {Roo.BasicDialog} this
30365          */
30366         "hide" : true,
30367         /**
30368          * @event beforeshow
30369          * Fires before this dialog is shown.
30370          * @param {Roo.BasicDialog} this
30371          */
30372         "beforeshow" : true,
30373         /**
30374          * @event show
30375          * Fires when this dialog is shown.
30376          * @param {Roo.BasicDialog} this
30377          */
30378         "show" : true
30379     });
30380     el.on("keydown", this.onKeyDown, this);
30381     el.on("mousedown", this.toFront, this);
30382     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30383     this.el.hide();
30384     Roo.DialogManager.register(this);
30385     Roo.BasicDialog.superclass.constructor.call(this);
30386 };
30387
30388 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30389     shadowOffset: Roo.isIE ? 6 : 5,
30390     minHeight: 80,
30391     minWidth: 200,
30392     minButtonWidth: 75,
30393     defaultButton: null,
30394     buttonAlign: "right",
30395     tabTag: 'div',
30396     firstShow: true,
30397
30398     /**
30399      * Sets the dialog title text
30400      * @param {String} text The title text to display
30401      * @return {Roo.BasicDialog} this
30402      */
30403     setTitle : function(text){
30404         this.header.update(text);
30405         return this;
30406     },
30407
30408     // private
30409     closeClick : function(){
30410         this.hide();
30411     },
30412
30413     // private
30414     collapseClick : function(){
30415         this[this.collapsed ? "expand" : "collapse"]();
30416     },
30417
30418     /**
30419      * Collapses the dialog to its minimized state (only the title bar is visible).
30420      * Equivalent to the user clicking the collapse dialog button.
30421      */
30422     collapse : function(){
30423         if(!this.collapsed){
30424             this.collapsed = true;
30425             this.el.addClass("x-dlg-collapsed");
30426             this.restoreHeight = this.el.getHeight();
30427             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30428         }
30429     },
30430
30431     /**
30432      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30433      * clicking the expand dialog button.
30434      */
30435     expand : function(){
30436         if(this.collapsed){
30437             this.collapsed = false;
30438             this.el.removeClass("x-dlg-collapsed");
30439             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30440         }
30441     },
30442
30443     /**
30444      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30445      * @return {Roo.TabPanel} The tabs component
30446      */
30447     initTabs : function(){
30448         var tabs = this.getTabs();
30449         while(tabs.getTab(0)){
30450             tabs.removeTab(0);
30451         }
30452         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30453             var dom = el.dom;
30454             tabs.addTab(Roo.id(dom), dom.title);
30455             dom.title = "";
30456         });
30457         tabs.activate(0);
30458         return tabs;
30459     },
30460
30461     // private
30462     beforeResize : function(){
30463         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30464     },
30465
30466     // private
30467     onResize : function(){
30468         this.refreshSize();
30469         this.syncBodyHeight();
30470         this.adjustAssets();
30471         this.focus();
30472         this.fireEvent("resize", this, this.size.width, this.size.height);
30473     },
30474
30475     // private
30476     onKeyDown : function(e){
30477         if(this.isVisible()){
30478             this.fireEvent("keydown", this, e);
30479         }
30480     },
30481
30482     /**
30483      * Resizes the dialog.
30484      * @param {Number} width
30485      * @param {Number} height
30486      * @return {Roo.BasicDialog} this
30487      */
30488     resizeTo : function(width, height){
30489         this.el.setSize(width, height);
30490         this.size = {width: width, height: height};
30491         this.syncBodyHeight();
30492         if(this.fixedcenter){
30493             this.center();
30494         }
30495         if(this.isVisible()){
30496             this.constrainXY();
30497             this.adjustAssets();
30498         }
30499         this.fireEvent("resize", this, width, height);
30500         return this;
30501     },
30502
30503
30504     /**
30505      * Resizes the dialog to fit the specified content size.
30506      * @param {Number} width
30507      * @param {Number} height
30508      * @return {Roo.BasicDialog} this
30509      */
30510     setContentSize : function(w, h){
30511         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30512         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30513         //if(!this.el.isBorderBox()){
30514             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30515             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30516         //}
30517         if(this.tabs){
30518             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30519             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30520         }
30521         this.resizeTo(w, h);
30522         return this;
30523     },
30524
30525     /**
30526      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30527      * executed in response to a particular key being pressed while the dialog is active.
30528      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30529      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30530      * @param {Function} fn The function to call
30531      * @param {Object} scope (optional) The scope of the function
30532      * @return {Roo.BasicDialog} this
30533      */
30534     addKeyListener : function(key, fn, scope){
30535         var keyCode, shift, ctrl, alt;
30536         if(typeof key == "object" && !(key instanceof Array)){
30537             keyCode = key["key"];
30538             shift = key["shift"];
30539             ctrl = key["ctrl"];
30540             alt = key["alt"];
30541         }else{
30542             keyCode = key;
30543         }
30544         var handler = function(dlg, e){
30545             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30546                 var k = e.getKey();
30547                 if(keyCode instanceof Array){
30548                     for(var i = 0, len = keyCode.length; i < len; i++){
30549                         if(keyCode[i] == k){
30550                           fn.call(scope || window, dlg, k, e);
30551                           return;
30552                         }
30553                     }
30554                 }else{
30555                     if(k == keyCode){
30556                         fn.call(scope || window, dlg, k, e);
30557                     }
30558                 }
30559             }
30560         };
30561         this.on("keydown", handler);
30562         return this;
30563     },
30564
30565     /**
30566      * Returns the TabPanel component (creates it if it doesn't exist).
30567      * Note: If you wish to simply check for the existence of tabs without creating them,
30568      * check for a null 'tabs' property.
30569      * @return {Roo.TabPanel} The tabs component
30570      */
30571     getTabs : function(){
30572         if(!this.tabs){
30573             this.el.addClass("x-dlg-auto-tabs");
30574             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30575             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30576         }
30577         return this.tabs;
30578     },
30579
30580     /**
30581      * Adds a button to the footer section of the dialog.
30582      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30583      * object or a valid Roo.DomHelper element config
30584      * @param {Function} handler The function called when the button is clicked
30585      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30586      * @return {Roo.Button} The new button
30587      */
30588     addButton : function(config, handler, scope){
30589         var dh = Roo.DomHelper;
30590         if(!this.footer){
30591             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30592         }
30593         if(!this.btnContainer){
30594             var tb = this.footer.createChild({
30595
30596                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30597                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30598             }, null, true);
30599             this.btnContainer = tb.firstChild.firstChild.firstChild;
30600         }
30601         var bconfig = {
30602             handler: handler,
30603             scope: scope,
30604             minWidth: this.minButtonWidth,
30605             hideParent:true
30606         };
30607         if(typeof config == "string"){
30608             bconfig.text = config;
30609         }else{
30610             if(config.tag){
30611                 bconfig.dhconfig = config;
30612             }else{
30613                 Roo.apply(bconfig, config);
30614             }
30615         }
30616         var fc = false;
30617         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30618             bconfig.position = Math.max(0, bconfig.position);
30619             fc = this.btnContainer.childNodes[bconfig.position];
30620         }
30621          
30622         var btn = new Roo.Button(
30623             fc ? 
30624                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30625                 : this.btnContainer.appendChild(document.createElement("td")),
30626             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30627             bconfig
30628         );
30629         this.syncBodyHeight();
30630         if(!this.buttons){
30631             /**
30632              * Array of all the buttons that have been added to this dialog via addButton
30633              * @type Array
30634              */
30635             this.buttons = [];
30636         }
30637         this.buttons.push(btn);
30638         return btn;
30639     },
30640
30641     /**
30642      * Sets the default button to be focused when the dialog is displayed.
30643      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30644      * @return {Roo.BasicDialog} this
30645      */
30646     setDefaultButton : function(btn){
30647         this.defaultButton = btn;
30648         return this;
30649     },
30650
30651     // private
30652     getHeaderFooterHeight : function(safe){
30653         var height = 0;
30654         if(this.header){
30655            height += this.header.getHeight();
30656         }
30657         if(this.footer){
30658            var fm = this.footer.getMargins();
30659             height += (this.footer.getHeight()+fm.top+fm.bottom);
30660         }
30661         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30662         height += this.centerBg.getPadding("tb");
30663         return height;
30664     },
30665
30666     // private
30667     syncBodyHeight : function()
30668     {
30669         var bd = this.body, // the text
30670             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30671             bw = this.bwrap;
30672         var height = this.size.height - this.getHeaderFooterHeight(false);
30673         bd.setHeight(height-bd.getMargins("tb"));
30674         var hh = this.header.getHeight();
30675         var h = this.size.height-hh;
30676         cb.setHeight(h);
30677         
30678         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30679         bw.setHeight(h-cb.getPadding("tb"));
30680         
30681         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30682         bd.setWidth(bw.getWidth(true));
30683         if(this.tabs){
30684             this.tabs.syncHeight();
30685             if(Roo.isIE){
30686                 this.tabs.el.repaint();
30687             }
30688         }
30689     },
30690
30691     /**
30692      * Restores the previous state of the dialog if Roo.state is configured.
30693      * @return {Roo.BasicDialog} this
30694      */
30695     restoreState : function(){
30696         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30697         if(box && box.width){
30698             this.xy = [box.x, box.y];
30699             this.resizeTo(box.width, box.height);
30700         }
30701         return this;
30702     },
30703
30704     // private
30705     beforeShow : function(){
30706         this.expand();
30707         if(this.fixedcenter){
30708             this.xy = this.el.getCenterXY(true);
30709         }
30710         if(this.modal){
30711             Roo.get(document.body).addClass("x-body-masked");
30712             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30713             this.mask.show();
30714         }
30715         this.constrainXY();
30716     },
30717
30718     // private
30719     animShow : function(){
30720         var b = Roo.get(this.animateTarget).getBox();
30721         this.proxy.setSize(b.width, b.height);
30722         this.proxy.setLocation(b.x, b.y);
30723         this.proxy.show();
30724         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30725                     true, .35, this.showEl.createDelegate(this));
30726     },
30727
30728     /**
30729      * Shows the dialog.
30730      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30731      * @return {Roo.BasicDialog} this
30732      */
30733     show : function(animateTarget){
30734         if (this.fireEvent("beforeshow", this) === false){
30735             return;
30736         }
30737         if(this.syncHeightBeforeShow){
30738             this.syncBodyHeight();
30739         }else if(this.firstShow){
30740             this.firstShow = false;
30741             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30742         }
30743         this.animateTarget = animateTarget || this.animateTarget;
30744         if(!this.el.isVisible()){
30745             this.beforeShow();
30746             if(this.animateTarget && Roo.get(this.animateTarget)){
30747                 this.animShow();
30748             }else{
30749                 this.showEl();
30750             }
30751         }
30752         return this;
30753     },
30754
30755     // private
30756     showEl : function(){
30757         this.proxy.hide();
30758         this.el.setXY(this.xy);
30759         this.el.show();
30760         this.adjustAssets(true);
30761         this.toFront();
30762         this.focus();
30763         // IE peekaboo bug - fix found by Dave Fenwick
30764         if(Roo.isIE){
30765             this.el.repaint();
30766         }
30767         this.fireEvent("show", this);
30768     },
30769
30770     /**
30771      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30772      * dialog itself will receive focus.
30773      */
30774     focus : function(){
30775         if(this.defaultButton){
30776             this.defaultButton.focus();
30777         }else{
30778             this.focusEl.focus();
30779         }
30780     },
30781
30782     // private
30783     constrainXY : function(){
30784         if(this.constraintoviewport !== false){
30785             if(!this.viewSize){
30786                 if(this.container){
30787                     var s = this.container.getSize();
30788                     this.viewSize = [s.width, s.height];
30789                 }else{
30790                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30791                 }
30792             }
30793             var s = Roo.get(this.container||document).getScroll();
30794
30795             var x = this.xy[0], y = this.xy[1];
30796             var w = this.size.width, h = this.size.height;
30797             var vw = this.viewSize[0], vh = this.viewSize[1];
30798             // only move it if it needs it
30799             var moved = false;
30800             // first validate right/bottom
30801             if(x + w > vw+s.left){
30802                 x = vw - w;
30803                 moved = true;
30804             }
30805             if(y + h > vh+s.top){
30806                 y = vh - h;
30807                 moved = true;
30808             }
30809             // then make sure top/left isn't negative
30810             if(x < s.left){
30811                 x = s.left;
30812                 moved = true;
30813             }
30814             if(y < s.top){
30815                 y = s.top;
30816                 moved = true;
30817             }
30818             if(moved){
30819                 // cache xy
30820                 this.xy = [x, y];
30821                 if(this.isVisible()){
30822                     this.el.setLocation(x, y);
30823                     this.adjustAssets();
30824                 }
30825             }
30826         }
30827     },
30828
30829     // private
30830     onDrag : function(){
30831         if(!this.proxyDrag){
30832             this.xy = this.el.getXY();
30833             this.adjustAssets();
30834         }
30835     },
30836
30837     // private
30838     adjustAssets : function(doShow){
30839         var x = this.xy[0], y = this.xy[1];
30840         var w = this.size.width, h = this.size.height;
30841         if(doShow === true){
30842             if(this.shadow){
30843                 this.shadow.show(this.el);
30844             }
30845             if(this.shim){
30846                 this.shim.show();
30847             }
30848         }
30849         if(this.shadow && this.shadow.isVisible()){
30850             this.shadow.show(this.el);
30851         }
30852         if(this.shim && this.shim.isVisible()){
30853             this.shim.setBounds(x, y, w, h);
30854         }
30855     },
30856
30857     // private
30858     adjustViewport : function(w, h){
30859         if(!w || !h){
30860             w = Roo.lib.Dom.getViewWidth();
30861             h = Roo.lib.Dom.getViewHeight();
30862         }
30863         // cache the size
30864         this.viewSize = [w, h];
30865         if(this.modal && this.mask.isVisible()){
30866             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30867             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30868         }
30869         if(this.isVisible()){
30870             this.constrainXY();
30871         }
30872     },
30873
30874     /**
30875      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30876      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30877      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30878      */
30879     destroy : function(removeEl){
30880         if(this.isVisible()){
30881             this.animateTarget = null;
30882             this.hide();
30883         }
30884         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30885         if(this.tabs){
30886             this.tabs.destroy(removeEl);
30887         }
30888         Roo.destroy(
30889              this.shim,
30890              this.proxy,
30891              this.resizer,
30892              this.close,
30893              this.mask
30894         );
30895         if(this.dd){
30896             this.dd.unreg();
30897         }
30898         if(this.buttons){
30899            for(var i = 0, len = this.buttons.length; i < len; i++){
30900                this.buttons[i].destroy();
30901            }
30902         }
30903         this.el.removeAllListeners();
30904         if(removeEl === true){
30905             this.el.update("");
30906             this.el.remove();
30907         }
30908         Roo.DialogManager.unregister(this);
30909     },
30910
30911     // private
30912     startMove : function(){
30913         if(this.proxyDrag){
30914             this.proxy.show();
30915         }
30916         if(this.constraintoviewport !== false){
30917             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30918         }
30919     },
30920
30921     // private
30922     endMove : function(){
30923         if(!this.proxyDrag){
30924             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30925         }else{
30926             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30927             this.proxy.hide();
30928         }
30929         this.refreshSize();
30930         this.adjustAssets();
30931         this.focus();
30932         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30933     },
30934
30935     /**
30936      * Brings this dialog to the front of any other visible dialogs
30937      * @return {Roo.BasicDialog} this
30938      */
30939     toFront : function(){
30940         Roo.DialogManager.bringToFront(this);
30941         return this;
30942     },
30943
30944     /**
30945      * Sends this dialog to the back (under) of any other visible dialogs
30946      * @return {Roo.BasicDialog} this
30947      */
30948     toBack : function(){
30949         Roo.DialogManager.sendToBack(this);
30950         return this;
30951     },
30952
30953     /**
30954      * Centers this dialog in the viewport
30955      * @return {Roo.BasicDialog} this
30956      */
30957     center : function(){
30958         var xy = this.el.getCenterXY(true);
30959         this.moveTo(xy[0], xy[1]);
30960         return this;
30961     },
30962
30963     /**
30964      * Moves the dialog's top-left corner to the specified point
30965      * @param {Number} x
30966      * @param {Number} y
30967      * @return {Roo.BasicDialog} this
30968      */
30969     moveTo : function(x, y){
30970         this.xy = [x,y];
30971         if(this.isVisible()){
30972             this.el.setXY(this.xy);
30973             this.adjustAssets();
30974         }
30975         return this;
30976     },
30977
30978     /**
30979      * Aligns the dialog to the specified element
30980      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30981      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30982      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30983      * @return {Roo.BasicDialog} this
30984      */
30985     alignTo : function(element, position, offsets){
30986         this.xy = this.el.getAlignToXY(element, position, offsets);
30987         if(this.isVisible()){
30988             this.el.setXY(this.xy);
30989             this.adjustAssets();
30990         }
30991         return this;
30992     },
30993
30994     /**
30995      * Anchors an element to another element and realigns it when the window is resized.
30996      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30997      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30998      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30999      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31000      * is a number, it is used as the buffer delay (defaults to 50ms).
31001      * @return {Roo.BasicDialog} this
31002      */
31003     anchorTo : function(el, alignment, offsets, monitorScroll){
31004         var action = function(){
31005             this.alignTo(el, alignment, offsets);
31006         };
31007         Roo.EventManager.onWindowResize(action, this);
31008         var tm = typeof monitorScroll;
31009         if(tm != 'undefined'){
31010             Roo.EventManager.on(window, 'scroll', action, this,
31011                 {buffer: tm == 'number' ? monitorScroll : 50});
31012         }
31013         action.call(this);
31014         return this;
31015     },
31016
31017     /**
31018      * Returns true if the dialog is visible
31019      * @return {Boolean}
31020      */
31021     isVisible : function(){
31022         return this.el.isVisible();
31023     },
31024
31025     // private
31026     animHide : function(callback){
31027         var b = Roo.get(this.animateTarget).getBox();
31028         this.proxy.show();
31029         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31030         this.el.hide();
31031         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31032                     this.hideEl.createDelegate(this, [callback]));
31033     },
31034
31035     /**
31036      * Hides the dialog.
31037      * @param {Function} callback (optional) Function to call when the dialog is hidden
31038      * @return {Roo.BasicDialog} this
31039      */
31040     hide : function(callback){
31041         if (this.fireEvent("beforehide", this) === false){
31042             return;
31043         }
31044         if(this.shadow){
31045             this.shadow.hide();
31046         }
31047         if(this.shim) {
31048           this.shim.hide();
31049         }
31050         // sometimes animateTarget seems to get set.. causing problems...
31051         // this just double checks..
31052         if(this.animateTarget && Roo.get(this.animateTarget)) {
31053            this.animHide(callback);
31054         }else{
31055             this.el.hide();
31056             this.hideEl(callback);
31057         }
31058         return this;
31059     },
31060
31061     // private
31062     hideEl : function(callback){
31063         this.proxy.hide();
31064         if(this.modal){
31065             this.mask.hide();
31066             Roo.get(document.body).removeClass("x-body-masked");
31067         }
31068         this.fireEvent("hide", this);
31069         if(typeof callback == "function"){
31070             callback();
31071         }
31072     },
31073
31074     // private
31075     hideAction : function(){
31076         this.setLeft("-10000px");
31077         this.setTop("-10000px");
31078         this.setStyle("visibility", "hidden");
31079     },
31080
31081     // private
31082     refreshSize : function(){
31083         this.size = this.el.getSize();
31084         this.xy = this.el.getXY();
31085         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31086     },
31087
31088     // private
31089     // z-index is managed by the DialogManager and may be overwritten at any time
31090     setZIndex : function(index){
31091         if(this.modal){
31092             this.mask.setStyle("z-index", index);
31093         }
31094         if(this.shim){
31095             this.shim.setStyle("z-index", ++index);
31096         }
31097         if(this.shadow){
31098             this.shadow.setZIndex(++index);
31099         }
31100         this.el.setStyle("z-index", ++index);
31101         if(this.proxy){
31102             this.proxy.setStyle("z-index", ++index);
31103         }
31104         if(this.resizer){
31105             this.resizer.proxy.setStyle("z-index", ++index);
31106         }
31107
31108         this.lastZIndex = index;
31109     },
31110
31111     /**
31112      * Returns the element for this dialog
31113      * @return {Roo.Element} The underlying dialog Element
31114      */
31115     getEl : function(){
31116         return this.el;
31117     }
31118 });
31119
31120 /**
31121  * @class Roo.DialogManager
31122  * Provides global access to BasicDialogs that have been created and
31123  * support for z-indexing (layering) multiple open dialogs.
31124  */
31125 Roo.DialogManager = function(){
31126     var list = {};
31127     var accessList = [];
31128     var front = null;
31129
31130     // private
31131     var sortDialogs = function(d1, d2){
31132         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31133     };
31134
31135     // private
31136     var orderDialogs = function(){
31137         accessList.sort(sortDialogs);
31138         var seed = Roo.DialogManager.zseed;
31139         for(var i = 0, len = accessList.length; i < len; i++){
31140             var dlg = accessList[i];
31141             if(dlg){
31142                 dlg.setZIndex(seed + (i*10));
31143             }
31144         }
31145     };
31146
31147     return {
31148         /**
31149          * The starting z-index for BasicDialogs (defaults to 9000)
31150          * @type Number The z-index value
31151          */
31152         zseed : 9000,
31153
31154         // private
31155         register : function(dlg){
31156             list[dlg.id] = dlg;
31157             accessList.push(dlg);
31158         },
31159
31160         // private
31161         unregister : function(dlg){
31162             delete list[dlg.id];
31163             var i=0;
31164             var len=0;
31165             if(!accessList.indexOf){
31166                 for(  i = 0, len = accessList.length; i < len; i++){
31167                     if(accessList[i] == dlg){
31168                         accessList.splice(i, 1);
31169                         return;
31170                     }
31171                 }
31172             }else{
31173                  i = accessList.indexOf(dlg);
31174                 if(i != -1){
31175                     accessList.splice(i, 1);
31176                 }
31177             }
31178         },
31179
31180         /**
31181          * Gets a registered dialog by id
31182          * @param {String/Object} id The id of the dialog or a dialog
31183          * @return {Roo.BasicDialog} this
31184          */
31185         get : function(id){
31186             return typeof id == "object" ? id : list[id];
31187         },
31188
31189         /**
31190          * Brings the specified dialog to the front
31191          * @param {String/Object} dlg The id of the dialog or a dialog
31192          * @return {Roo.BasicDialog} this
31193          */
31194         bringToFront : function(dlg){
31195             dlg = this.get(dlg);
31196             if(dlg != front){
31197                 front = dlg;
31198                 dlg._lastAccess = new Date().getTime();
31199                 orderDialogs();
31200             }
31201             return dlg;
31202         },
31203
31204         /**
31205          * Sends the specified dialog to the back
31206          * @param {String/Object} dlg The id of the dialog or a dialog
31207          * @return {Roo.BasicDialog} this
31208          */
31209         sendToBack : function(dlg){
31210             dlg = this.get(dlg);
31211             dlg._lastAccess = -(new Date().getTime());
31212             orderDialogs();
31213             return dlg;
31214         },
31215
31216         /**
31217          * Hides all dialogs
31218          */
31219         hideAll : function(){
31220             for(var id in list){
31221                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31222                     list[id].hide();
31223                 }
31224             }
31225         }
31226     };
31227 }();
31228
31229 /**
31230  * @class Roo.LayoutDialog
31231  * @extends Roo.BasicDialog
31232  * Dialog which provides adjustments for working with a layout in a Dialog.
31233  * Add your necessary layout config options to the dialog's config.<br>
31234  * Example usage (including a nested layout):
31235  * <pre><code>
31236 if(!dialog){
31237     dialog = new Roo.LayoutDialog("download-dlg", {
31238         modal: true,
31239         width:600,
31240         height:450,
31241         shadow:true,
31242         minWidth:500,
31243         minHeight:350,
31244         autoTabs:true,
31245         proxyDrag:true,
31246         // layout config merges with the dialog config
31247         center:{
31248             tabPosition: "top",
31249             alwaysShowTabs: true
31250         }
31251     });
31252     dialog.addKeyListener(27, dialog.hide, dialog);
31253     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31254     dialog.addButton("Build It!", this.getDownload, this);
31255
31256     // we can even add nested layouts
31257     var innerLayout = new Roo.BorderLayout("dl-inner", {
31258         east: {
31259             initialSize: 200,
31260             autoScroll:true,
31261             split:true
31262         },
31263         center: {
31264             autoScroll:true
31265         }
31266     });
31267     innerLayout.beginUpdate();
31268     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31269     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31270     innerLayout.endUpdate(true);
31271
31272     var layout = dialog.getLayout();
31273     layout.beginUpdate();
31274     layout.add("center", new Roo.ContentPanel("standard-panel",
31275                         {title: "Download the Source", fitToFrame:true}));
31276     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31277                {title: "Build your own roo.js"}));
31278     layout.getRegion("center").showPanel(sp);
31279     layout.endUpdate();
31280 }
31281 </code></pre>
31282     * @constructor
31283     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31284     * @param {Object} config configuration options
31285   */
31286 Roo.LayoutDialog = function(el, cfg){
31287     
31288     var config=  cfg;
31289     if (typeof(cfg) == 'undefined') {
31290         config = Roo.apply({}, el);
31291         // not sure why we use documentElement here.. - it should always be body.
31292         // IE7 borks horribly if we use documentElement.
31293         // webkit also does not like documentElement - it creates a body element...
31294         el = Roo.get( document.body || document.documentElement ).createChild();
31295         //config.autoCreate = true;
31296     }
31297     
31298     
31299     config.autoTabs = false;
31300     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31301     this.body.setStyle({overflow:"hidden", position:"relative"});
31302     this.layout = new Roo.BorderLayout(this.body.dom, config);
31303     this.layout.monitorWindowResize = false;
31304     this.el.addClass("x-dlg-auto-layout");
31305     // fix case when center region overwrites center function
31306     this.center = Roo.BasicDialog.prototype.center;
31307     this.on("show", this.layout.layout, this.layout, true);
31308     if (config.items) {
31309         var xitems = config.items;
31310         delete config.items;
31311         Roo.each(xitems, this.addxtype, this);
31312     }
31313     
31314     
31315 };
31316 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31317     /**
31318      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31319      * @deprecated
31320      */
31321     endUpdate : function(){
31322         this.layout.endUpdate();
31323     },
31324
31325     /**
31326      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31327      *  @deprecated
31328      */
31329     beginUpdate : function(){
31330         this.layout.beginUpdate();
31331     },
31332
31333     /**
31334      * Get the BorderLayout for this dialog
31335      * @return {Roo.BorderLayout}
31336      */
31337     getLayout : function(){
31338         return this.layout;
31339     },
31340
31341     showEl : function(){
31342         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31343         if(Roo.isIE7){
31344             this.layout.layout();
31345         }
31346     },
31347
31348     // private
31349     // Use the syncHeightBeforeShow config option to control this automatically
31350     syncBodyHeight : function(){
31351         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31352         if(this.layout){this.layout.layout();}
31353     },
31354     
31355       /**
31356      * Add an xtype element (actually adds to the layout.)
31357      * @return {Object} xdata xtype object data.
31358      */
31359     
31360     addxtype : function(c) {
31361         return this.layout.addxtype(c);
31362     }
31363 });/*
31364  * Based on:
31365  * Ext JS Library 1.1.1
31366  * Copyright(c) 2006-2007, Ext JS, LLC.
31367  *
31368  * Originally Released Under LGPL - original licence link has changed is not relivant.
31369  *
31370  * Fork - LGPL
31371  * <script type="text/javascript">
31372  */
31373  
31374 /**
31375  * @class Roo.MessageBox
31376  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31377  * Example usage:
31378  *<pre><code>
31379 // Basic alert:
31380 Roo.Msg.alert('Status', 'Changes saved successfully.');
31381
31382 // Prompt for user data:
31383 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31384     if (btn == 'ok'){
31385         // process text value...
31386     }
31387 });
31388
31389 // Show a dialog using config options:
31390 Roo.Msg.show({
31391    title:'Save Changes?',
31392    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31393    buttons: Roo.Msg.YESNOCANCEL,
31394    fn: processResult,
31395    animEl: 'elId'
31396 });
31397 </code></pre>
31398  * @singleton
31399  */
31400 Roo.MessageBox = function(){
31401     var dlg, opt, mask, waitTimer;
31402     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31403     var buttons, activeTextEl, bwidth;
31404
31405     // private
31406     var handleButton = function(button){
31407         dlg.hide();
31408         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31409     };
31410
31411     // private
31412     var handleHide = function(){
31413         if(opt && opt.cls){
31414             dlg.el.removeClass(opt.cls);
31415         }
31416         if(waitTimer){
31417             Roo.TaskMgr.stop(waitTimer);
31418             waitTimer = null;
31419         }
31420     };
31421
31422     // private
31423     var updateButtons = function(b){
31424         var width = 0;
31425         if(!b){
31426             buttons["ok"].hide();
31427             buttons["cancel"].hide();
31428             buttons["yes"].hide();
31429             buttons["no"].hide();
31430             dlg.footer.dom.style.display = 'none';
31431             return width;
31432         }
31433         dlg.footer.dom.style.display = '';
31434         for(var k in buttons){
31435             if(typeof buttons[k] != "function"){
31436                 if(b[k]){
31437                     buttons[k].show();
31438                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31439                     width += buttons[k].el.getWidth()+15;
31440                 }else{
31441                     buttons[k].hide();
31442                 }
31443             }
31444         }
31445         return width;
31446     };
31447
31448     // private
31449     var handleEsc = function(d, k, e){
31450         if(opt && opt.closable !== false){
31451             dlg.hide();
31452         }
31453         if(e){
31454             e.stopEvent();
31455         }
31456     };
31457
31458     return {
31459         /**
31460          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31461          * @return {Roo.BasicDialog} The BasicDialog element
31462          */
31463         getDialog : function(){
31464            if(!dlg){
31465                 dlg = new Roo.BasicDialog("x-msg-box", {
31466                     autoCreate : true,
31467                     shadow: true,
31468                     draggable: true,
31469                     resizable:false,
31470                     constraintoviewport:false,
31471                     fixedcenter:true,
31472                     collapsible : false,
31473                     shim:true,
31474                     modal: true,
31475                     width:400, height:100,
31476                     buttonAlign:"center",
31477                     closeClick : function(){
31478                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31479                             handleButton("no");
31480                         }else{
31481                             handleButton("cancel");
31482                         }
31483                     }
31484                 });
31485                 dlg.on("hide", handleHide);
31486                 mask = dlg.mask;
31487                 dlg.addKeyListener(27, handleEsc);
31488                 buttons = {};
31489                 var bt = this.buttonText;
31490                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31491                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31492                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31493                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31494                 bodyEl = dlg.body.createChild({
31495
31496                     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>'
31497                 });
31498                 msgEl = bodyEl.dom.firstChild;
31499                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31500                 textboxEl.enableDisplayMode();
31501                 textboxEl.addKeyListener([10,13], function(){
31502                     if(dlg.isVisible() && opt && opt.buttons){
31503                         if(opt.buttons.ok){
31504                             handleButton("ok");
31505                         }else if(opt.buttons.yes){
31506                             handleButton("yes");
31507                         }
31508                     }
31509                 });
31510                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31511                 textareaEl.enableDisplayMode();
31512                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31513                 progressEl.enableDisplayMode();
31514                 var pf = progressEl.dom.firstChild;
31515                 if (pf) {
31516                     pp = Roo.get(pf.firstChild);
31517                     pp.setHeight(pf.offsetHeight);
31518                 }
31519                 
31520             }
31521             return dlg;
31522         },
31523
31524         /**
31525          * Updates the message box body text
31526          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31527          * the XHTML-compliant non-breaking space character '&amp;#160;')
31528          * @return {Roo.MessageBox} This message box
31529          */
31530         updateText : function(text){
31531             if(!dlg.isVisible() && !opt.width){
31532                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31533             }
31534             msgEl.innerHTML = text || '&#160;';
31535       
31536             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31537             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31538             var w = Math.max(
31539                     Math.min(opt.width || cw , this.maxWidth), 
31540                     Math.max(opt.minWidth || this.minWidth, bwidth)
31541             );
31542             if(opt.prompt){
31543                 activeTextEl.setWidth(w);
31544             }
31545             if(dlg.isVisible()){
31546                 dlg.fixedcenter = false;
31547             }
31548             // to big, make it scroll. = But as usual stupid IE does not support
31549             // !important..
31550             
31551             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31552                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31553                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31554             } else {
31555                 bodyEl.dom.style.height = '';
31556                 bodyEl.dom.style.overflowY = '';
31557             }
31558             if (cw > w) {
31559                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31560             } else {
31561                 bodyEl.dom.style.overflowX = '';
31562             }
31563             
31564             dlg.setContentSize(w, bodyEl.getHeight());
31565             if(dlg.isVisible()){
31566                 dlg.fixedcenter = true;
31567             }
31568             return this;
31569         },
31570
31571         /**
31572          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31573          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31574          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31575          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31576          * @return {Roo.MessageBox} This message box
31577          */
31578         updateProgress : function(value, text){
31579             if(text){
31580                 this.updateText(text);
31581             }
31582             if (pp) { // weird bug on my firefox - for some reason this is not defined
31583                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31584             }
31585             return this;
31586         },        
31587
31588         /**
31589          * Returns true if the message box is currently displayed
31590          * @return {Boolean} True if the message box is visible, else false
31591          */
31592         isVisible : function(){
31593             return dlg && dlg.isVisible();  
31594         },
31595
31596         /**
31597          * Hides the message box if it is displayed
31598          */
31599         hide : function(){
31600             if(this.isVisible()){
31601                 dlg.hide();
31602             }  
31603         },
31604
31605         /**
31606          * Displays a new message box, or reinitializes an existing message box, based on the config options
31607          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31608          * The following config object properties are supported:
31609          * <pre>
31610 Property    Type             Description
31611 ----------  ---------------  ------------------------------------------------------------------------------------
31612 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31613                                    closes (defaults to undefined)
31614 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31615                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31616 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31617                                    progress and wait dialogs will ignore this property and always hide the
31618                                    close button as they can only be closed programmatically.
31619 cls               String           A custom CSS class to apply to the message box element
31620 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31621                                    displayed (defaults to 75)
31622 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31623                                    function will be btn (the name of the button that was clicked, if applicable,
31624                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31625                                    Progress and wait dialogs will ignore this option since they do not respond to
31626                                    user actions and can only be closed programmatically, so any required function
31627                                    should be called by the same code after it closes the dialog.
31628 icon              String           A CSS class that provides a background image to be used as an icon for
31629                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31630 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31631 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31632 modal             Boolean          False to allow user interaction with the page while the message box is
31633                                    displayed (defaults to true)
31634 msg               String           A string that will replace the existing message box body text (defaults
31635                                    to the XHTML-compliant non-breaking space character '&#160;')
31636 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31637 progress          Boolean          True to display a progress bar (defaults to false)
31638 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31639 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31640 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31641 title             String           The title text
31642 value             String           The string value to set into the active textbox element if displayed
31643 wait              Boolean          True to display a progress bar (defaults to false)
31644 width             Number           The width of the dialog in pixels
31645 </pre>
31646          *
31647          * Example usage:
31648          * <pre><code>
31649 Roo.Msg.show({
31650    title: 'Address',
31651    msg: 'Please enter your address:',
31652    width: 300,
31653    buttons: Roo.MessageBox.OKCANCEL,
31654    multiline: true,
31655    fn: saveAddress,
31656    animEl: 'addAddressBtn'
31657 });
31658 </code></pre>
31659          * @param {Object} config Configuration options
31660          * @return {Roo.MessageBox} This message box
31661          */
31662         show : function(options)
31663         {
31664             
31665             // this causes nightmares if you show one dialog after another
31666             // especially on callbacks..
31667              
31668             if(this.isVisible()){
31669                 
31670                 this.hide();
31671                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31672                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31673                 Roo.log("New Dialog Message:" +  options.msg )
31674                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31675                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31676                 
31677             }
31678             var d = this.getDialog();
31679             opt = options;
31680             d.setTitle(opt.title || "&#160;");
31681             d.close.setDisplayed(opt.closable !== false);
31682             activeTextEl = textboxEl;
31683             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31684             if(opt.prompt){
31685                 if(opt.multiline){
31686                     textboxEl.hide();
31687                     textareaEl.show();
31688                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31689                         opt.multiline : this.defaultTextHeight);
31690                     activeTextEl = textareaEl;
31691                 }else{
31692                     textboxEl.show();
31693                     textareaEl.hide();
31694                 }
31695             }else{
31696                 textboxEl.hide();
31697                 textareaEl.hide();
31698             }
31699             progressEl.setDisplayed(opt.progress === true);
31700             this.updateProgress(0);
31701             activeTextEl.dom.value = opt.value || "";
31702             if(opt.prompt){
31703                 dlg.setDefaultButton(activeTextEl);
31704             }else{
31705                 var bs = opt.buttons;
31706                 var db = null;
31707                 if(bs && bs.ok){
31708                     db = buttons["ok"];
31709                 }else if(bs && bs.yes){
31710                     db = buttons["yes"];
31711                 }
31712                 dlg.setDefaultButton(db);
31713             }
31714             bwidth = updateButtons(opt.buttons);
31715             this.updateText(opt.msg);
31716             if(opt.cls){
31717                 d.el.addClass(opt.cls);
31718             }
31719             d.proxyDrag = opt.proxyDrag === true;
31720             d.modal = opt.modal !== false;
31721             d.mask = opt.modal !== false ? mask : false;
31722             if(!d.isVisible()){
31723                 // force it to the end of the z-index stack so it gets a cursor in FF
31724                 document.body.appendChild(dlg.el.dom);
31725                 d.animateTarget = null;
31726                 d.show(options.animEl);
31727             }
31728             return this;
31729         },
31730
31731         /**
31732          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31733          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31734          * and closing the message box when the process is complete.
31735          * @param {String} title The title bar text
31736          * @param {String} msg The message box body text
31737          * @return {Roo.MessageBox} This message box
31738          */
31739         progress : function(title, msg){
31740             this.show({
31741                 title : title,
31742                 msg : msg,
31743                 buttons: false,
31744                 progress:true,
31745                 closable:false,
31746                 minWidth: this.minProgressWidth,
31747                 modal : true
31748             });
31749             return this;
31750         },
31751
31752         /**
31753          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31754          * If a callback function is passed it will be called after the user clicks the button, and the
31755          * id of the button that was clicked will be passed as the only parameter to the callback
31756          * (could also be the top-right close button).
31757          * @param {String} title The title bar text
31758          * @param {String} msg The message box body text
31759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31760          * @param {Object} scope (optional) The scope of the callback function
31761          * @return {Roo.MessageBox} This message box
31762          */
31763         alert : function(title, msg, fn, scope){
31764             this.show({
31765                 title : title,
31766                 msg : msg,
31767                 buttons: this.OK,
31768                 fn: fn,
31769                 scope : scope,
31770                 modal : true
31771             });
31772             return this;
31773         },
31774
31775         /**
31776          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31777          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31778          * You are responsible for closing the message box when the process is complete.
31779          * @param {String} msg The message box body text
31780          * @param {String} title (optional) The title bar text
31781          * @return {Roo.MessageBox} This message box
31782          */
31783         wait : function(msg, title){
31784             this.show({
31785                 title : title,
31786                 msg : msg,
31787                 buttons: false,
31788                 closable:false,
31789                 progress:true,
31790                 modal:true,
31791                 width:300,
31792                 wait:true
31793             });
31794             waitTimer = Roo.TaskMgr.start({
31795                 run: function(i){
31796                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31797                 },
31798                 interval: 1000
31799             });
31800             return this;
31801         },
31802
31803         /**
31804          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31805          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31806          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31807          * @param {String} title The title bar text
31808          * @param {String} msg The message box body text
31809          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31810          * @param {Object} scope (optional) The scope of the callback function
31811          * @return {Roo.MessageBox} This message box
31812          */
31813         confirm : function(title, msg, fn, scope){
31814             this.show({
31815                 title : title,
31816                 msg : msg,
31817                 buttons: this.YESNO,
31818                 fn: fn,
31819                 scope : scope,
31820                 modal : true
31821             });
31822             return this;
31823         },
31824
31825         /**
31826          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31827          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31828          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31829          * (could also be the top-right close button) and the text that was entered will be passed as the two
31830          * parameters to the callback.
31831          * @param {String} title The title bar text
31832          * @param {String} msg The message box body text
31833          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31834          * @param {Object} scope (optional) The scope of the callback function
31835          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31836          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31837          * @return {Roo.MessageBox} This message box
31838          */
31839         prompt : function(title, msg, fn, scope, multiline){
31840             this.show({
31841                 title : title,
31842                 msg : msg,
31843                 buttons: this.OKCANCEL,
31844                 fn: fn,
31845                 minWidth:250,
31846                 scope : scope,
31847                 prompt:true,
31848                 multiline: multiline,
31849                 modal : true
31850             });
31851             return this;
31852         },
31853
31854         /**
31855          * Button config that displays a single OK button
31856          * @type Object
31857          */
31858         OK : {ok:true},
31859         /**
31860          * Button config that displays Yes and No buttons
31861          * @type Object
31862          */
31863         YESNO : {yes:true, no:true},
31864         /**
31865          * Button config that displays OK and Cancel buttons
31866          * @type Object
31867          */
31868         OKCANCEL : {ok:true, cancel:true},
31869         /**
31870          * Button config that displays Yes, No and Cancel buttons
31871          * @type Object
31872          */
31873         YESNOCANCEL : {yes:true, no:true, cancel:true},
31874
31875         /**
31876          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31877          * @type Number
31878          */
31879         defaultTextHeight : 75,
31880         /**
31881          * The maximum width in pixels of the message box (defaults to 600)
31882          * @type Number
31883          */
31884         maxWidth : 600,
31885         /**
31886          * The minimum width in pixels of the message box (defaults to 100)
31887          * @type Number
31888          */
31889         minWidth : 100,
31890         /**
31891          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31892          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31893          * @type Number
31894          */
31895         minProgressWidth : 250,
31896         /**
31897          * An object containing the default button text strings that can be overriden for localized language support.
31898          * Supported properties are: ok, cancel, yes and no.
31899          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31900          * @type Object
31901          */
31902         buttonText : {
31903             ok : "OK",
31904             cancel : "Cancel",
31905             yes : "Yes",
31906             no : "No"
31907         }
31908     };
31909 }();
31910
31911 /**
31912  * Shorthand for {@link Roo.MessageBox}
31913  */
31914 Roo.Msg = Roo.MessageBox;/*
31915  * Based on:
31916  * Ext JS Library 1.1.1
31917  * Copyright(c) 2006-2007, Ext JS, LLC.
31918  *
31919  * Originally Released Under LGPL - original licence link has changed is not relivant.
31920  *
31921  * Fork - LGPL
31922  * <script type="text/javascript">
31923  */
31924 /**
31925  * @class Roo.QuickTips
31926  * Provides attractive and customizable tooltips for any element.
31927  * @singleton
31928  */
31929 Roo.QuickTips = function(){
31930     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31931     var ce, bd, xy, dd;
31932     var visible = false, disabled = true, inited = false;
31933     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31934     
31935     var onOver = function(e){
31936         if(disabled){
31937             return;
31938         }
31939         var t = e.getTarget();
31940         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31941             return;
31942         }
31943         if(ce && t == ce.el){
31944             clearTimeout(hideProc);
31945             return;
31946         }
31947         if(t && tagEls[t.id]){
31948             tagEls[t.id].el = t;
31949             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31950             return;
31951         }
31952         var ttp, et = Roo.fly(t);
31953         var ns = cfg.namespace;
31954         if(tm.interceptTitles && t.title){
31955             ttp = t.title;
31956             t.qtip = ttp;
31957             t.removeAttribute("title");
31958             e.preventDefault();
31959         }else{
31960             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31961         }
31962         if(ttp){
31963             showProc = show.defer(tm.showDelay, tm, [{
31964                 el: t, 
31965                 text: ttp, 
31966                 width: et.getAttributeNS(ns, cfg.width),
31967                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31968                 title: et.getAttributeNS(ns, cfg.title),
31969                     cls: et.getAttributeNS(ns, cfg.cls)
31970             }]);
31971         }
31972     };
31973     
31974     var onOut = function(e){
31975         clearTimeout(showProc);
31976         var t = e.getTarget();
31977         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31978             hideProc = setTimeout(hide, tm.hideDelay);
31979         }
31980     };
31981     
31982     var onMove = function(e){
31983         if(disabled){
31984             return;
31985         }
31986         xy = e.getXY();
31987         xy[1] += 18;
31988         if(tm.trackMouse && ce){
31989             el.setXY(xy);
31990         }
31991     };
31992     
31993     var onDown = function(e){
31994         clearTimeout(showProc);
31995         clearTimeout(hideProc);
31996         if(!e.within(el)){
31997             if(tm.hideOnClick){
31998                 hide();
31999                 tm.disable();
32000                 tm.enable.defer(100, tm);
32001             }
32002         }
32003     };
32004     
32005     var getPad = function(){
32006         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32007     };
32008
32009     var show = function(o){
32010         if(disabled){
32011             return;
32012         }
32013         clearTimeout(dismissProc);
32014         ce = o;
32015         if(removeCls){ // in case manually hidden
32016             el.removeClass(removeCls);
32017             removeCls = null;
32018         }
32019         if(ce.cls){
32020             el.addClass(ce.cls);
32021             removeCls = ce.cls;
32022         }
32023         if(ce.title){
32024             tipTitle.update(ce.title);
32025             tipTitle.show();
32026         }else{
32027             tipTitle.update('');
32028             tipTitle.hide();
32029         }
32030         el.dom.style.width  = tm.maxWidth+'px';
32031         //tipBody.dom.style.width = '';
32032         tipBodyText.update(o.text);
32033         var p = getPad(), w = ce.width;
32034         if(!w){
32035             var td = tipBodyText.dom;
32036             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32037             if(aw > tm.maxWidth){
32038                 w = tm.maxWidth;
32039             }else if(aw < tm.minWidth){
32040                 w = tm.minWidth;
32041             }else{
32042                 w = aw;
32043             }
32044         }
32045         //tipBody.setWidth(w);
32046         el.setWidth(parseInt(w, 10) + p);
32047         if(ce.autoHide === false){
32048             close.setDisplayed(true);
32049             if(dd){
32050                 dd.unlock();
32051             }
32052         }else{
32053             close.setDisplayed(false);
32054             if(dd){
32055                 dd.lock();
32056             }
32057         }
32058         if(xy){
32059             el.avoidY = xy[1]-18;
32060             el.setXY(xy);
32061         }
32062         if(tm.animate){
32063             el.setOpacity(.1);
32064             el.setStyle("visibility", "visible");
32065             el.fadeIn({callback: afterShow});
32066         }else{
32067             afterShow();
32068         }
32069     };
32070     
32071     var afterShow = function(){
32072         if(ce){
32073             el.show();
32074             esc.enable();
32075             if(tm.autoDismiss && ce.autoHide !== false){
32076                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32077             }
32078         }
32079     };
32080     
32081     var hide = function(noanim){
32082         clearTimeout(dismissProc);
32083         clearTimeout(hideProc);
32084         ce = null;
32085         if(el.isVisible()){
32086             esc.disable();
32087             if(noanim !== true && tm.animate){
32088                 el.fadeOut({callback: afterHide});
32089             }else{
32090                 afterHide();
32091             } 
32092         }
32093     };
32094     
32095     var afterHide = function(){
32096         el.hide();
32097         if(removeCls){
32098             el.removeClass(removeCls);
32099             removeCls = null;
32100         }
32101     };
32102     
32103     return {
32104         /**
32105         * @cfg {Number} minWidth
32106         * The minimum width of the quick tip (defaults to 40)
32107         */
32108        minWidth : 40,
32109         /**
32110         * @cfg {Number} maxWidth
32111         * The maximum width of the quick tip (defaults to 300)
32112         */
32113        maxWidth : 300,
32114         /**
32115         * @cfg {Boolean} interceptTitles
32116         * True to automatically use the element's DOM title value if available (defaults to false)
32117         */
32118        interceptTitles : false,
32119         /**
32120         * @cfg {Boolean} trackMouse
32121         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32122         */
32123        trackMouse : false,
32124         /**
32125         * @cfg {Boolean} hideOnClick
32126         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32127         */
32128        hideOnClick : true,
32129         /**
32130         * @cfg {Number} showDelay
32131         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32132         */
32133        showDelay : 500,
32134         /**
32135         * @cfg {Number} hideDelay
32136         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32137         */
32138        hideDelay : 200,
32139         /**
32140         * @cfg {Boolean} autoHide
32141         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32142         * Used in conjunction with hideDelay.
32143         */
32144        autoHide : true,
32145         /**
32146         * @cfg {Boolean}
32147         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32148         * (defaults to true).  Used in conjunction with autoDismissDelay.
32149         */
32150        autoDismiss : true,
32151         /**
32152         * @cfg {Number}
32153         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32154         */
32155        autoDismissDelay : 5000,
32156        /**
32157         * @cfg {Boolean} animate
32158         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32159         */
32160        animate : false,
32161
32162        /**
32163         * @cfg {String} title
32164         * Title text to display (defaults to '').  This can be any valid HTML markup.
32165         */
32166         title: '',
32167        /**
32168         * @cfg {String} text
32169         * Body text to display (defaults to '').  This can be any valid HTML markup.
32170         */
32171         text : '',
32172        /**
32173         * @cfg {String} cls
32174         * A CSS class to apply to the base quick tip element (defaults to '').
32175         */
32176         cls : '',
32177        /**
32178         * @cfg {Number} width
32179         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32180         * minWidth or maxWidth.
32181         */
32182         width : null,
32183
32184     /**
32185      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32186      * or display QuickTips in a page.
32187      */
32188        init : function(){
32189           tm = Roo.QuickTips;
32190           cfg = tm.tagConfig;
32191           if(!inited){
32192               if(!Roo.isReady){ // allow calling of init() before onReady
32193                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32194                   return;
32195               }
32196               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32197               el.fxDefaults = {stopFx: true};
32198               // maximum custom styling
32199               //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>');
32200               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>');              
32201               tipTitle = el.child('h3');
32202               tipTitle.enableDisplayMode("block");
32203               tipBody = el.child('div.x-tip-bd');
32204               tipBodyText = el.child('div.x-tip-bd-inner');
32205               //bdLeft = el.child('div.x-tip-bd-left');
32206               //bdRight = el.child('div.x-tip-bd-right');
32207               close = el.child('div.x-tip-close');
32208               close.enableDisplayMode("block");
32209               close.on("click", hide);
32210               var d = Roo.get(document);
32211               d.on("mousedown", onDown);
32212               d.on("mouseover", onOver);
32213               d.on("mouseout", onOut);
32214               d.on("mousemove", onMove);
32215               esc = d.addKeyListener(27, hide);
32216               esc.disable();
32217               if(Roo.dd.DD){
32218                   dd = el.initDD("default", null, {
32219                       onDrag : function(){
32220                           el.sync();  
32221                       }
32222                   });
32223                   dd.setHandleElId(tipTitle.id);
32224                   dd.lock();
32225               }
32226               inited = true;
32227           }
32228           this.enable(); 
32229        },
32230
32231     /**
32232      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32233      * are supported:
32234      * <pre>
32235 Property    Type                   Description
32236 ----------  ---------------------  ------------------------------------------------------------------------
32237 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32238      * </ul>
32239      * @param {Object} config The config object
32240      */
32241        register : function(config){
32242            var cs = config instanceof Array ? config : arguments;
32243            for(var i = 0, len = cs.length; i < len; i++) {
32244                var c = cs[i];
32245                var target = c.target;
32246                if(target){
32247                    if(target instanceof Array){
32248                        for(var j = 0, jlen = target.length; j < jlen; j++){
32249                            tagEls[target[j]] = c;
32250                        }
32251                    }else{
32252                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32253                    }
32254                }
32255            }
32256        },
32257
32258     /**
32259      * Removes this quick tip from its element and destroys it.
32260      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32261      */
32262        unregister : function(el){
32263            delete tagEls[Roo.id(el)];
32264        },
32265
32266     /**
32267      * Enable this quick tip.
32268      */
32269        enable : function(){
32270            if(inited && disabled){
32271                locks.pop();
32272                if(locks.length < 1){
32273                    disabled = false;
32274                }
32275            }
32276        },
32277
32278     /**
32279      * Disable this quick tip.
32280      */
32281        disable : function(){
32282           disabled = true;
32283           clearTimeout(showProc);
32284           clearTimeout(hideProc);
32285           clearTimeout(dismissProc);
32286           if(ce){
32287               hide(true);
32288           }
32289           locks.push(1);
32290        },
32291
32292     /**
32293      * Returns true if the quick tip is enabled, else false.
32294      */
32295        isEnabled : function(){
32296             return !disabled;
32297        },
32298
32299         // private
32300        tagConfig : {
32301            namespace : "ext",
32302            attribute : "qtip",
32303            width : "width",
32304            target : "target",
32305            title : "qtitle",
32306            hide : "hide",
32307            cls : "qclass"
32308        }
32309    };
32310 }();
32311
32312 // backwards compat
32313 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32314  * Based on:
32315  * Ext JS Library 1.1.1
32316  * Copyright(c) 2006-2007, Ext JS, LLC.
32317  *
32318  * Originally Released Under LGPL - original licence link has changed is not relivant.
32319  *
32320  * Fork - LGPL
32321  * <script type="text/javascript">
32322  */
32323  
32324
32325 /**
32326  * @class Roo.tree.TreePanel
32327  * @extends Roo.data.Tree
32328
32329  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32330  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32331  * @cfg {Boolean} enableDD true to enable drag and drop
32332  * @cfg {Boolean} enableDrag true to enable just drag
32333  * @cfg {Boolean} enableDrop true to enable just drop
32334  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32335  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32336  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32337  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32338  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32339  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32340  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32341  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32342  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32343  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32344  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32345  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32346  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32347  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32348  * @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>
32349  * @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>
32350  * 
32351  * @constructor
32352  * @param {String/HTMLElement/Element} el The container element
32353  * @param {Object} config
32354  */
32355 Roo.tree.TreePanel = function(el, config){
32356     var root = false;
32357     var loader = false;
32358     if (config.root) {
32359         root = config.root;
32360         delete config.root;
32361     }
32362     if (config.loader) {
32363         loader = config.loader;
32364         delete config.loader;
32365     }
32366     
32367     Roo.apply(this, config);
32368     Roo.tree.TreePanel.superclass.constructor.call(this);
32369     this.el = Roo.get(el);
32370     this.el.addClass('x-tree');
32371     //console.log(root);
32372     if (root) {
32373         this.setRootNode( Roo.factory(root, Roo.tree));
32374     }
32375     if (loader) {
32376         this.loader = Roo.factory(loader, Roo.tree);
32377     }
32378    /**
32379     * Read-only. The id of the container element becomes this TreePanel's id.
32380     */
32381     this.id = this.el.id;
32382     this.addEvents({
32383         /**
32384         * @event beforeload
32385         * Fires before a node is loaded, return false to cancel
32386         * @param {Node} node The node being loaded
32387         */
32388         "beforeload" : true,
32389         /**
32390         * @event load
32391         * Fires when a node is loaded
32392         * @param {Node} node The node that was loaded
32393         */
32394         "load" : true,
32395         /**
32396         * @event textchange
32397         * Fires when the text for a node is changed
32398         * @param {Node} node The node
32399         * @param {String} text The new text
32400         * @param {String} oldText The old text
32401         */
32402         "textchange" : true,
32403         /**
32404         * @event beforeexpand
32405         * Fires before a node is expanded, return false to cancel.
32406         * @param {Node} node The node
32407         * @param {Boolean} deep
32408         * @param {Boolean} anim
32409         */
32410         "beforeexpand" : true,
32411         /**
32412         * @event beforecollapse
32413         * Fires before a node is collapsed, return false to cancel.
32414         * @param {Node} node The node
32415         * @param {Boolean} deep
32416         * @param {Boolean} anim
32417         */
32418         "beforecollapse" : true,
32419         /**
32420         * @event expand
32421         * Fires when a node is expanded
32422         * @param {Node} node The node
32423         */
32424         "expand" : true,
32425         /**
32426         * @event disabledchange
32427         * Fires when the disabled status of a node changes
32428         * @param {Node} node The node
32429         * @param {Boolean} disabled
32430         */
32431         "disabledchange" : true,
32432         /**
32433         * @event collapse
32434         * Fires when a node is collapsed
32435         * @param {Node} node The node
32436         */
32437         "collapse" : true,
32438         /**
32439         * @event beforeclick
32440         * Fires before click processing on a node. Return false to cancel the default action.
32441         * @param {Node} node The node
32442         * @param {Roo.EventObject} e The event object
32443         */
32444         "beforeclick":true,
32445         /**
32446         * @event checkchange
32447         * Fires when a node with a checkbox's checked property changes
32448         * @param {Node} this This node
32449         * @param {Boolean} checked
32450         */
32451         "checkchange":true,
32452         /**
32453         * @event click
32454         * Fires when a node is clicked
32455         * @param {Node} node The node
32456         * @param {Roo.EventObject} e The event object
32457         */
32458         "click":true,
32459         /**
32460         * @event dblclick
32461         * Fires when a node is double clicked
32462         * @param {Node} node The node
32463         * @param {Roo.EventObject} e The event object
32464         */
32465         "dblclick":true,
32466         /**
32467         * @event contextmenu
32468         * Fires when a node is right clicked
32469         * @param {Node} node The node
32470         * @param {Roo.EventObject} e The event object
32471         */
32472         "contextmenu":true,
32473         /**
32474         * @event beforechildrenrendered
32475         * Fires right before the child nodes for a node are rendered
32476         * @param {Node} node The node
32477         */
32478         "beforechildrenrendered":true,
32479         /**
32480         * @event startdrag
32481         * Fires when a node starts being dragged
32482         * @param {Roo.tree.TreePanel} this
32483         * @param {Roo.tree.TreeNode} node
32484         * @param {event} e The raw browser event
32485         */ 
32486        "startdrag" : true,
32487        /**
32488         * @event enddrag
32489         * Fires when a drag operation is complete
32490         * @param {Roo.tree.TreePanel} this
32491         * @param {Roo.tree.TreeNode} node
32492         * @param {event} e The raw browser event
32493         */
32494        "enddrag" : true,
32495        /**
32496         * @event dragdrop
32497         * Fires when a dragged node is dropped on a valid DD target
32498         * @param {Roo.tree.TreePanel} this
32499         * @param {Roo.tree.TreeNode} node
32500         * @param {DD} dd The dd it was dropped on
32501         * @param {event} e The raw browser event
32502         */
32503        "dragdrop" : true,
32504        /**
32505         * @event beforenodedrop
32506         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32507         * passed to handlers has the following properties:<br />
32508         * <ul style="padding:5px;padding-left:16px;">
32509         * <li>tree - The TreePanel</li>
32510         * <li>target - The node being targeted for the drop</li>
32511         * <li>data - The drag data from the drag source</li>
32512         * <li>point - The point of the drop - append, above or below</li>
32513         * <li>source - The drag source</li>
32514         * <li>rawEvent - Raw mouse event</li>
32515         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32516         * to be inserted by setting them on this object.</li>
32517         * <li>cancel - Set this to true to cancel the drop.</li>
32518         * </ul>
32519         * @param {Object} dropEvent
32520         */
32521        "beforenodedrop" : true,
32522        /**
32523         * @event nodedrop
32524         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32525         * passed to handlers has the following properties:<br />
32526         * <ul style="padding:5px;padding-left:16px;">
32527         * <li>tree - The TreePanel</li>
32528         * <li>target - The node being targeted for the drop</li>
32529         * <li>data - The drag data from the drag source</li>
32530         * <li>point - The point of the drop - append, above or below</li>
32531         * <li>source - The drag source</li>
32532         * <li>rawEvent - Raw mouse event</li>
32533         * <li>dropNode - Dropped node(s).</li>
32534         * </ul>
32535         * @param {Object} dropEvent
32536         */
32537        "nodedrop" : true,
32538         /**
32539         * @event nodedragover
32540         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32541         * passed to handlers has the following properties:<br />
32542         * <ul style="padding:5px;padding-left:16px;">
32543         * <li>tree - The TreePanel</li>
32544         * <li>target - The node being targeted for the drop</li>
32545         * <li>data - The drag data from the drag source</li>
32546         * <li>point - The point of the drop - append, above or below</li>
32547         * <li>source - The drag source</li>
32548         * <li>rawEvent - Raw mouse event</li>
32549         * <li>dropNode - Drop node(s) provided by the source.</li>
32550         * <li>cancel - Set this to true to signal drop not allowed.</li>
32551         * </ul>
32552         * @param {Object} dragOverEvent
32553         */
32554        "nodedragover" : true
32555         
32556     });
32557     if(this.singleExpand){
32558        this.on("beforeexpand", this.restrictExpand, this);
32559     }
32560     if (this.editor) {
32561         this.editor.tree = this;
32562         this.editor = Roo.factory(this.editor, Roo.tree);
32563     }
32564     
32565     if (this.selModel) {
32566         this.selModel = Roo.factory(this.selModel, Roo.tree);
32567     }
32568    
32569 };
32570 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32571     rootVisible : true,
32572     animate: Roo.enableFx,
32573     lines : true,
32574     enableDD : false,
32575     hlDrop : Roo.enableFx,
32576   
32577     renderer: false,
32578     
32579     rendererTip: false,
32580     // private
32581     restrictExpand : function(node){
32582         var p = node.parentNode;
32583         if(p){
32584             if(p.expandedChild && p.expandedChild.parentNode == p){
32585                 p.expandedChild.collapse();
32586             }
32587             p.expandedChild = node;
32588         }
32589     },
32590
32591     // private override
32592     setRootNode : function(node){
32593         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32594         if(!this.rootVisible){
32595             node.ui = new Roo.tree.RootTreeNodeUI(node);
32596         }
32597         return node;
32598     },
32599
32600     /**
32601      * Returns the container element for this TreePanel
32602      */
32603     getEl : function(){
32604         return this.el;
32605     },
32606
32607     /**
32608      * Returns the default TreeLoader for this TreePanel
32609      */
32610     getLoader : function(){
32611         return this.loader;
32612     },
32613
32614     /**
32615      * Expand all nodes
32616      */
32617     expandAll : function(){
32618         this.root.expand(true);
32619     },
32620
32621     /**
32622      * Collapse all nodes
32623      */
32624     collapseAll : function(){
32625         this.root.collapse(true);
32626     },
32627
32628     /**
32629      * Returns the selection model used by this TreePanel
32630      */
32631     getSelectionModel : function(){
32632         if(!this.selModel){
32633             this.selModel = new Roo.tree.DefaultSelectionModel();
32634         }
32635         return this.selModel;
32636     },
32637
32638     /**
32639      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32640      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32641      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32642      * @return {Array}
32643      */
32644     getChecked : function(a, startNode){
32645         startNode = startNode || this.root;
32646         var r = [];
32647         var f = function(){
32648             if(this.attributes.checked){
32649                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32650             }
32651         }
32652         startNode.cascade(f);
32653         return r;
32654     },
32655
32656     /**
32657      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32658      * @param {String} path
32659      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32660      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32661      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32662      */
32663     expandPath : function(path, attr, callback){
32664         attr = attr || "id";
32665         var keys = path.split(this.pathSeparator);
32666         var curNode = this.root;
32667         if(curNode.attributes[attr] != keys[1]){ // invalid root
32668             if(callback){
32669                 callback(false, null);
32670             }
32671             return;
32672         }
32673         var index = 1;
32674         var f = function(){
32675             if(++index == keys.length){
32676                 if(callback){
32677                     callback(true, curNode);
32678                 }
32679                 return;
32680             }
32681             var c = curNode.findChild(attr, keys[index]);
32682             if(!c){
32683                 if(callback){
32684                     callback(false, curNode);
32685                 }
32686                 return;
32687             }
32688             curNode = c;
32689             c.expand(false, false, f);
32690         };
32691         curNode.expand(false, false, f);
32692     },
32693
32694     /**
32695      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32696      * @param {String} path
32697      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32698      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32699      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32700      */
32701     selectPath : function(path, attr, callback){
32702         attr = attr || "id";
32703         var keys = path.split(this.pathSeparator);
32704         var v = keys.pop();
32705         if(keys.length > 0){
32706             var f = function(success, node){
32707                 if(success && node){
32708                     var n = node.findChild(attr, v);
32709                     if(n){
32710                         n.select();
32711                         if(callback){
32712                             callback(true, n);
32713                         }
32714                     }else if(callback){
32715                         callback(false, n);
32716                     }
32717                 }else{
32718                     if(callback){
32719                         callback(false, n);
32720                     }
32721                 }
32722             };
32723             this.expandPath(keys.join(this.pathSeparator), attr, f);
32724         }else{
32725             this.root.select();
32726             if(callback){
32727                 callback(true, this.root);
32728             }
32729         }
32730     },
32731
32732     getTreeEl : function(){
32733         return this.el;
32734     },
32735
32736     /**
32737      * Trigger rendering of this TreePanel
32738      */
32739     render : function(){
32740         if (this.innerCt) {
32741             return this; // stop it rendering more than once!!
32742         }
32743         
32744         this.innerCt = this.el.createChild({tag:"ul",
32745                cls:"x-tree-root-ct " +
32746                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32747
32748         if(this.containerScroll){
32749             Roo.dd.ScrollManager.register(this.el);
32750         }
32751         if((this.enableDD || this.enableDrop) && !this.dropZone){
32752            /**
32753             * The dropZone used by this tree if drop is enabled
32754             * @type Roo.tree.TreeDropZone
32755             */
32756              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32757                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32758            });
32759         }
32760         if((this.enableDD || this.enableDrag) && !this.dragZone){
32761            /**
32762             * The dragZone used by this tree if drag is enabled
32763             * @type Roo.tree.TreeDragZone
32764             */
32765             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32766                ddGroup: this.ddGroup || "TreeDD",
32767                scroll: this.ddScroll
32768            });
32769         }
32770         this.getSelectionModel().init(this);
32771         if (!this.root) {
32772             Roo.log("ROOT not set in tree");
32773             return this;
32774         }
32775         this.root.render();
32776         if(!this.rootVisible){
32777             this.root.renderChildren();
32778         }
32779         return this;
32780     }
32781 });/*
32782  * Based on:
32783  * Ext JS Library 1.1.1
32784  * Copyright(c) 2006-2007, Ext JS, LLC.
32785  *
32786  * Originally Released Under LGPL - original licence link has changed is not relivant.
32787  *
32788  * Fork - LGPL
32789  * <script type="text/javascript">
32790  */
32791  
32792
32793 /**
32794  * @class Roo.tree.DefaultSelectionModel
32795  * @extends Roo.util.Observable
32796  * The default single selection for a TreePanel.
32797  * @param {Object} cfg Configuration
32798  */
32799 Roo.tree.DefaultSelectionModel = function(cfg){
32800    this.selNode = null;
32801    
32802    
32803    
32804    this.addEvents({
32805        /**
32806         * @event selectionchange
32807         * Fires when the selected node changes
32808         * @param {DefaultSelectionModel} this
32809         * @param {TreeNode} node the new selection
32810         */
32811        "selectionchange" : true,
32812
32813        /**
32814         * @event beforeselect
32815         * Fires before the selected node changes, return false to cancel the change
32816         * @param {DefaultSelectionModel} this
32817         * @param {TreeNode} node the new selection
32818         * @param {TreeNode} node the old selection
32819         */
32820        "beforeselect" : true
32821    });
32822    
32823     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32824 };
32825
32826 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32827     init : function(tree){
32828         this.tree = tree;
32829         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32830         tree.on("click", this.onNodeClick, this);
32831     },
32832     
32833     onNodeClick : function(node, e){
32834         if (e.ctrlKey && this.selNode == node)  {
32835             this.unselect(node);
32836             return;
32837         }
32838         this.select(node);
32839     },
32840     
32841     /**
32842      * Select a node.
32843      * @param {TreeNode} node The node to select
32844      * @return {TreeNode} The selected node
32845      */
32846     select : function(node){
32847         var last = this.selNode;
32848         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32849             if(last){
32850                 last.ui.onSelectedChange(false);
32851             }
32852             this.selNode = node;
32853             node.ui.onSelectedChange(true);
32854             this.fireEvent("selectionchange", this, node, last);
32855         }
32856         return node;
32857     },
32858     
32859     /**
32860      * Deselect a node.
32861      * @param {TreeNode} node The node to unselect
32862      */
32863     unselect : function(node){
32864         if(this.selNode == node){
32865             this.clearSelections();
32866         }    
32867     },
32868     
32869     /**
32870      * Clear all selections
32871      */
32872     clearSelections : function(){
32873         var n = this.selNode;
32874         if(n){
32875             n.ui.onSelectedChange(false);
32876             this.selNode = null;
32877             this.fireEvent("selectionchange", this, null);
32878         }
32879         return n;
32880     },
32881     
32882     /**
32883      * Get the selected node
32884      * @return {TreeNode} The selected node
32885      */
32886     getSelectedNode : function(){
32887         return this.selNode;    
32888     },
32889     
32890     /**
32891      * Returns true if the node is selected
32892      * @param {TreeNode} node The node to check
32893      * @return {Boolean}
32894      */
32895     isSelected : function(node){
32896         return this.selNode == node;  
32897     },
32898
32899     /**
32900      * Selects the node above the selected node in the tree, intelligently walking the nodes
32901      * @return TreeNode The new selection
32902      */
32903     selectPrevious : function(){
32904         var s = this.selNode || this.lastSelNode;
32905         if(!s){
32906             return null;
32907         }
32908         var ps = s.previousSibling;
32909         if(ps){
32910             if(!ps.isExpanded() || ps.childNodes.length < 1){
32911                 return this.select(ps);
32912             } else{
32913                 var lc = ps.lastChild;
32914                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32915                     lc = lc.lastChild;
32916                 }
32917                 return this.select(lc);
32918             }
32919         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32920             return this.select(s.parentNode);
32921         }
32922         return null;
32923     },
32924
32925     /**
32926      * Selects the node above the selected node in the tree, intelligently walking the nodes
32927      * @return TreeNode The new selection
32928      */
32929     selectNext : function(){
32930         var s = this.selNode || this.lastSelNode;
32931         if(!s){
32932             return null;
32933         }
32934         if(s.firstChild && s.isExpanded()){
32935              return this.select(s.firstChild);
32936          }else if(s.nextSibling){
32937              return this.select(s.nextSibling);
32938          }else if(s.parentNode){
32939             var newS = null;
32940             s.parentNode.bubble(function(){
32941                 if(this.nextSibling){
32942                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32943                     return false;
32944                 }
32945             });
32946             return newS;
32947          }
32948         return null;
32949     },
32950
32951     onKeyDown : function(e){
32952         var s = this.selNode || this.lastSelNode;
32953         // undesirable, but required
32954         var sm = this;
32955         if(!s){
32956             return;
32957         }
32958         var k = e.getKey();
32959         switch(k){
32960              case e.DOWN:
32961                  e.stopEvent();
32962                  this.selectNext();
32963              break;
32964              case e.UP:
32965                  e.stopEvent();
32966                  this.selectPrevious();
32967              break;
32968              case e.RIGHT:
32969                  e.preventDefault();
32970                  if(s.hasChildNodes()){
32971                      if(!s.isExpanded()){
32972                          s.expand();
32973                      }else if(s.firstChild){
32974                          this.select(s.firstChild, e);
32975                      }
32976                  }
32977              break;
32978              case e.LEFT:
32979                  e.preventDefault();
32980                  if(s.hasChildNodes() && s.isExpanded()){
32981                      s.collapse();
32982                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32983                      this.select(s.parentNode, e);
32984                  }
32985              break;
32986         };
32987     }
32988 });
32989
32990 /**
32991  * @class Roo.tree.MultiSelectionModel
32992  * @extends Roo.util.Observable
32993  * Multi selection for a TreePanel.
32994  * @param {Object} cfg Configuration
32995  */
32996 Roo.tree.MultiSelectionModel = function(){
32997    this.selNodes = [];
32998    this.selMap = {};
32999    this.addEvents({
33000        /**
33001         * @event selectionchange
33002         * Fires when the selected nodes change
33003         * @param {MultiSelectionModel} this
33004         * @param {Array} nodes Array of the selected nodes
33005         */
33006        "selectionchange" : true
33007    });
33008    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33009    
33010 };
33011
33012 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33013     init : function(tree){
33014         this.tree = tree;
33015         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33016         tree.on("click", this.onNodeClick, this);
33017     },
33018     
33019     onNodeClick : function(node, e){
33020         this.select(node, e, e.ctrlKey);
33021     },
33022     
33023     /**
33024      * Select a node.
33025      * @param {TreeNode} node The node to select
33026      * @param {EventObject} e (optional) An event associated with the selection
33027      * @param {Boolean} keepExisting True to retain existing selections
33028      * @return {TreeNode} The selected node
33029      */
33030     select : function(node, e, keepExisting){
33031         if(keepExisting !== true){
33032             this.clearSelections(true);
33033         }
33034         if(this.isSelected(node)){
33035             this.lastSelNode = node;
33036             return node;
33037         }
33038         this.selNodes.push(node);
33039         this.selMap[node.id] = node;
33040         this.lastSelNode = node;
33041         node.ui.onSelectedChange(true);
33042         this.fireEvent("selectionchange", this, this.selNodes);
33043         return node;
33044     },
33045     
33046     /**
33047      * Deselect a node.
33048      * @param {TreeNode} node The node to unselect
33049      */
33050     unselect : function(node){
33051         if(this.selMap[node.id]){
33052             node.ui.onSelectedChange(false);
33053             var sn = this.selNodes;
33054             var index = -1;
33055             if(sn.indexOf){
33056                 index = sn.indexOf(node);
33057             }else{
33058                 for(var i = 0, len = sn.length; i < len; i++){
33059                     if(sn[i] == node){
33060                         index = i;
33061                         break;
33062                     }
33063                 }
33064             }
33065             if(index != -1){
33066                 this.selNodes.splice(index, 1);
33067             }
33068             delete this.selMap[node.id];
33069             this.fireEvent("selectionchange", this, this.selNodes);
33070         }
33071     },
33072     
33073     /**
33074      * Clear all selections
33075      */
33076     clearSelections : function(suppressEvent){
33077         var sn = this.selNodes;
33078         if(sn.length > 0){
33079             for(var i = 0, len = sn.length; i < len; i++){
33080                 sn[i].ui.onSelectedChange(false);
33081             }
33082             this.selNodes = [];
33083             this.selMap = {};
33084             if(suppressEvent !== true){
33085                 this.fireEvent("selectionchange", this, this.selNodes);
33086             }
33087         }
33088     },
33089     
33090     /**
33091      * Returns true if the node is selected
33092      * @param {TreeNode} node The node to check
33093      * @return {Boolean}
33094      */
33095     isSelected : function(node){
33096         return this.selMap[node.id] ? true : false;  
33097     },
33098     
33099     /**
33100      * Returns an array of the selected nodes
33101      * @return {Array}
33102      */
33103     getSelectedNodes : function(){
33104         return this.selNodes;    
33105     },
33106
33107     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33108
33109     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33110
33111     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33112 });/*
33113  * Based on:
33114  * Ext JS Library 1.1.1
33115  * Copyright(c) 2006-2007, Ext JS, LLC.
33116  *
33117  * Originally Released Under LGPL - original licence link has changed is not relivant.
33118  *
33119  * Fork - LGPL
33120  * <script type="text/javascript">
33121  */
33122  
33123 /**
33124  * @class Roo.tree.TreeNode
33125  * @extends Roo.data.Node
33126  * @cfg {String} text The text for this node
33127  * @cfg {Boolean} expanded true to start the node expanded
33128  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33129  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33130  * @cfg {Boolean} disabled true to start the node disabled
33131  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33132  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33133  * @cfg {String} cls A css class to be added to the node
33134  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33135  * @cfg {String} href URL of the link used for the node (defaults to #)
33136  * @cfg {String} hrefTarget target frame for the link
33137  * @cfg {String} qtip An Ext QuickTip for the node
33138  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33139  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33140  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33141  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33142  * (defaults to undefined with no checkbox rendered)
33143  * @constructor
33144  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33145  */
33146 Roo.tree.TreeNode = function(attributes){
33147     attributes = attributes || {};
33148     if(typeof attributes == "string"){
33149         attributes = {text: attributes};
33150     }
33151     this.childrenRendered = false;
33152     this.rendered = false;
33153     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33154     this.expanded = attributes.expanded === true;
33155     this.isTarget = attributes.isTarget !== false;
33156     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33157     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33158
33159     /**
33160      * Read-only. The text for this node. To change it use setText().
33161      * @type String
33162      */
33163     this.text = attributes.text;
33164     /**
33165      * True if this node is disabled.
33166      * @type Boolean
33167      */
33168     this.disabled = attributes.disabled === true;
33169
33170     this.addEvents({
33171         /**
33172         * @event textchange
33173         * Fires when the text for this node is changed
33174         * @param {Node} this This node
33175         * @param {String} text The new text
33176         * @param {String} oldText The old text
33177         */
33178         "textchange" : true,
33179         /**
33180         * @event beforeexpand
33181         * Fires before this node is expanded, return false to cancel.
33182         * @param {Node} this This node
33183         * @param {Boolean} deep
33184         * @param {Boolean} anim
33185         */
33186         "beforeexpand" : true,
33187         /**
33188         * @event beforecollapse
33189         * Fires before this node is collapsed, return false to cancel.
33190         * @param {Node} this This node
33191         * @param {Boolean} deep
33192         * @param {Boolean} anim
33193         */
33194         "beforecollapse" : true,
33195         /**
33196         * @event expand
33197         * Fires when this node is expanded
33198         * @param {Node} this This node
33199         */
33200         "expand" : true,
33201         /**
33202         * @event disabledchange
33203         * Fires when the disabled status of this node changes
33204         * @param {Node} this This node
33205         * @param {Boolean} disabled
33206         */
33207         "disabledchange" : true,
33208         /**
33209         * @event collapse
33210         * Fires when this node is collapsed
33211         * @param {Node} this This node
33212         */
33213         "collapse" : true,
33214         /**
33215         * @event beforeclick
33216         * Fires before click processing. Return false to cancel the default action.
33217         * @param {Node} this This node
33218         * @param {Roo.EventObject} e The event object
33219         */
33220         "beforeclick":true,
33221         /**
33222         * @event checkchange
33223         * Fires when a node with a checkbox's checked property changes
33224         * @param {Node} this This node
33225         * @param {Boolean} checked
33226         */
33227         "checkchange":true,
33228         /**
33229         * @event click
33230         * Fires when this node is clicked
33231         * @param {Node} this This node
33232         * @param {Roo.EventObject} e The event object
33233         */
33234         "click":true,
33235         /**
33236         * @event dblclick
33237         * Fires when this node is double clicked
33238         * @param {Node} this This node
33239         * @param {Roo.EventObject} e The event object
33240         */
33241         "dblclick":true,
33242         /**
33243         * @event contextmenu
33244         * Fires when this node is right clicked
33245         * @param {Node} this This node
33246         * @param {Roo.EventObject} e The event object
33247         */
33248         "contextmenu":true,
33249         /**
33250         * @event beforechildrenrendered
33251         * Fires right before the child nodes for this node are rendered
33252         * @param {Node} this This node
33253         */
33254         "beforechildrenrendered":true
33255     });
33256
33257     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33258
33259     /**
33260      * Read-only. The UI for this node
33261      * @type TreeNodeUI
33262      */
33263     this.ui = new uiClass(this);
33264     
33265     // finally support items[]
33266     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33267         return;
33268     }
33269     
33270     
33271     Roo.each(this.attributes.items, function(c) {
33272         this.appendChild(Roo.factory(c,Roo.Tree));
33273     }, this);
33274     delete this.attributes.items;
33275     
33276     
33277     
33278 };
33279 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33280     preventHScroll: true,
33281     /**
33282      * Returns true if this node is expanded
33283      * @return {Boolean}
33284      */
33285     isExpanded : function(){
33286         return this.expanded;
33287     },
33288
33289     /**
33290      * Returns the UI object for this node
33291      * @return {TreeNodeUI}
33292      */
33293     getUI : function(){
33294         return this.ui;
33295     },
33296
33297     // private override
33298     setFirstChild : function(node){
33299         var of = this.firstChild;
33300         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33301         if(this.childrenRendered && of && node != of){
33302             of.renderIndent(true, true);
33303         }
33304         if(this.rendered){
33305             this.renderIndent(true, true);
33306         }
33307     },
33308
33309     // private override
33310     setLastChild : function(node){
33311         var ol = this.lastChild;
33312         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33313         if(this.childrenRendered && ol && node != ol){
33314             ol.renderIndent(true, true);
33315         }
33316         if(this.rendered){
33317             this.renderIndent(true, true);
33318         }
33319     },
33320
33321     // these methods are overridden to provide lazy rendering support
33322     // private override
33323     appendChild : function()
33324     {
33325         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33326         if(node && this.childrenRendered){
33327             node.render();
33328         }
33329         this.ui.updateExpandIcon();
33330         return node;
33331     },
33332
33333     // private override
33334     removeChild : function(node){
33335         this.ownerTree.getSelectionModel().unselect(node);
33336         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33337         // if it's been rendered remove dom node
33338         if(this.childrenRendered){
33339             node.ui.remove();
33340         }
33341         if(this.childNodes.length < 1){
33342             this.collapse(false, false);
33343         }else{
33344             this.ui.updateExpandIcon();
33345         }
33346         if(!this.firstChild) {
33347             this.childrenRendered = false;
33348         }
33349         return node;
33350     },
33351
33352     // private override
33353     insertBefore : function(node, refNode){
33354         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33355         if(newNode && refNode && this.childrenRendered){
33356             node.render();
33357         }
33358         this.ui.updateExpandIcon();
33359         return newNode;
33360     },
33361
33362     /**
33363      * Sets the text for this node
33364      * @param {String} text
33365      */
33366     setText : function(text){
33367         var oldText = this.text;
33368         this.text = text;
33369         this.attributes.text = text;
33370         if(this.rendered){ // event without subscribing
33371             this.ui.onTextChange(this, text, oldText);
33372         }
33373         this.fireEvent("textchange", this, text, oldText);
33374     },
33375
33376     /**
33377      * Triggers selection of this node
33378      */
33379     select : function(){
33380         this.getOwnerTree().getSelectionModel().select(this);
33381     },
33382
33383     /**
33384      * Triggers deselection of this node
33385      */
33386     unselect : function(){
33387         this.getOwnerTree().getSelectionModel().unselect(this);
33388     },
33389
33390     /**
33391      * Returns true if this node is selected
33392      * @return {Boolean}
33393      */
33394     isSelected : function(){
33395         return this.getOwnerTree().getSelectionModel().isSelected(this);
33396     },
33397
33398     /**
33399      * Expand this node.
33400      * @param {Boolean} deep (optional) True to expand all children as well
33401      * @param {Boolean} anim (optional) false to cancel the default animation
33402      * @param {Function} callback (optional) A callback to be called when
33403      * expanding this node completes (does not wait for deep expand to complete).
33404      * Called with 1 parameter, this node.
33405      */
33406     expand : function(deep, anim, callback){
33407         if(!this.expanded){
33408             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33409                 return;
33410             }
33411             if(!this.childrenRendered){
33412                 this.renderChildren();
33413             }
33414             this.expanded = true;
33415             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33416                 this.ui.animExpand(function(){
33417                     this.fireEvent("expand", this);
33418                     if(typeof callback == "function"){
33419                         callback(this);
33420                     }
33421                     if(deep === true){
33422                         this.expandChildNodes(true);
33423                     }
33424                 }.createDelegate(this));
33425                 return;
33426             }else{
33427                 this.ui.expand();
33428                 this.fireEvent("expand", this);
33429                 if(typeof callback == "function"){
33430                     callback(this);
33431                 }
33432             }
33433         }else{
33434            if(typeof callback == "function"){
33435                callback(this);
33436            }
33437         }
33438         if(deep === true){
33439             this.expandChildNodes(true);
33440         }
33441     },
33442
33443     isHiddenRoot : function(){
33444         return this.isRoot && !this.getOwnerTree().rootVisible;
33445     },
33446
33447     /**
33448      * Collapse this node.
33449      * @param {Boolean} deep (optional) True to collapse all children as well
33450      * @param {Boolean} anim (optional) false to cancel the default animation
33451      */
33452     collapse : function(deep, anim){
33453         if(this.expanded && !this.isHiddenRoot()){
33454             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33455                 return;
33456             }
33457             this.expanded = false;
33458             if((this.getOwnerTree().animate && anim !== false) || anim){
33459                 this.ui.animCollapse(function(){
33460                     this.fireEvent("collapse", this);
33461                     if(deep === true){
33462                         this.collapseChildNodes(true);
33463                     }
33464                 }.createDelegate(this));
33465                 return;
33466             }else{
33467                 this.ui.collapse();
33468                 this.fireEvent("collapse", this);
33469             }
33470         }
33471         if(deep === true){
33472             var cs = this.childNodes;
33473             for(var i = 0, len = cs.length; i < len; i++) {
33474                 cs[i].collapse(true, false);
33475             }
33476         }
33477     },
33478
33479     // private
33480     delayedExpand : function(delay){
33481         if(!this.expandProcId){
33482             this.expandProcId = this.expand.defer(delay, this);
33483         }
33484     },
33485
33486     // private
33487     cancelExpand : function(){
33488         if(this.expandProcId){
33489             clearTimeout(this.expandProcId);
33490         }
33491         this.expandProcId = false;
33492     },
33493
33494     /**
33495      * Toggles expanded/collapsed state of the node
33496      */
33497     toggle : function(){
33498         if(this.expanded){
33499             this.collapse();
33500         }else{
33501             this.expand();
33502         }
33503     },
33504
33505     /**
33506      * Ensures all parent nodes are expanded
33507      */
33508     ensureVisible : function(callback){
33509         var tree = this.getOwnerTree();
33510         tree.expandPath(this.parentNode.getPath(), false, function(){
33511             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33512             Roo.callback(callback);
33513         }.createDelegate(this));
33514     },
33515
33516     /**
33517      * Expand all child nodes
33518      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33519      */
33520     expandChildNodes : function(deep){
33521         var cs = this.childNodes;
33522         for(var i = 0, len = cs.length; i < len; i++) {
33523                 cs[i].expand(deep);
33524         }
33525     },
33526
33527     /**
33528      * Collapse all child nodes
33529      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33530      */
33531     collapseChildNodes : function(deep){
33532         var cs = this.childNodes;
33533         for(var i = 0, len = cs.length; i < len; i++) {
33534                 cs[i].collapse(deep);
33535         }
33536     },
33537
33538     /**
33539      * Disables this node
33540      */
33541     disable : function(){
33542         this.disabled = true;
33543         this.unselect();
33544         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33545             this.ui.onDisableChange(this, true);
33546         }
33547         this.fireEvent("disabledchange", this, true);
33548     },
33549
33550     /**
33551      * Enables this node
33552      */
33553     enable : function(){
33554         this.disabled = false;
33555         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33556             this.ui.onDisableChange(this, false);
33557         }
33558         this.fireEvent("disabledchange", this, false);
33559     },
33560
33561     // private
33562     renderChildren : function(suppressEvent){
33563         if(suppressEvent !== false){
33564             this.fireEvent("beforechildrenrendered", this);
33565         }
33566         var cs = this.childNodes;
33567         for(var i = 0, len = cs.length; i < len; i++){
33568             cs[i].render(true);
33569         }
33570         this.childrenRendered = true;
33571     },
33572
33573     // private
33574     sort : function(fn, scope){
33575         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33576         if(this.childrenRendered){
33577             var cs = this.childNodes;
33578             for(var i = 0, len = cs.length; i < len; i++){
33579                 cs[i].render(true);
33580             }
33581         }
33582     },
33583
33584     // private
33585     render : function(bulkRender){
33586         this.ui.render(bulkRender);
33587         if(!this.rendered){
33588             this.rendered = true;
33589             if(this.expanded){
33590                 this.expanded = false;
33591                 this.expand(false, false);
33592             }
33593         }
33594     },
33595
33596     // private
33597     renderIndent : function(deep, refresh){
33598         if(refresh){
33599             this.ui.childIndent = null;
33600         }
33601         this.ui.renderIndent();
33602         if(deep === true && this.childrenRendered){
33603             var cs = this.childNodes;
33604             for(var i = 0, len = cs.length; i < len; i++){
33605                 cs[i].renderIndent(true, refresh);
33606             }
33607         }
33608     }
33609 });/*
33610  * Based on:
33611  * Ext JS Library 1.1.1
33612  * Copyright(c) 2006-2007, Ext JS, LLC.
33613  *
33614  * Originally Released Under LGPL - original licence link has changed is not relivant.
33615  *
33616  * Fork - LGPL
33617  * <script type="text/javascript">
33618  */
33619  
33620 /**
33621  * @class Roo.tree.AsyncTreeNode
33622  * @extends Roo.tree.TreeNode
33623  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33624  * @constructor
33625  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33626  */
33627  Roo.tree.AsyncTreeNode = function(config){
33628     this.loaded = false;
33629     this.loading = false;
33630     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33631     /**
33632     * @event beforeload
33633     * Fires before this node is loaded, return false to cancel
33634     * @param {Node} this This node
33635     */
33636     this.addEvents({'beforeload':true, 'load': true});
33637     /**
33638     * @event load
33639     * Fires when this node is loaded
33640     * @param {Node} this This node
33641     */
33642     /**
33643      * The loader used by this node (defaults to using the tree's defined loader)
33644      * @type TreeLoader
33645      * @property loader
33646      */
33647 };
33648 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33649     expand : function(deep, anim, callback){
33650         if(this.loading){ // if an async load is already running, waiting til it's done
33651             var timer;
33652             var f = function(){
33653                 if(!this.loading){ // done loading
33654                     clearInterval(timer);
33655                     this.expand(deep, anim, callback);
33656                 }
33657             }.createDelegate(this);
33658             timer = setInterval(f, 200);
33659             return;
33660         }
33661         if(!this.loaded){
33662             if(this.fireEvent("beforeload", this) === false){
33663                 return;
33664             }
33665             this.loading = true;
33666             this.ui.beforeLoad(this);
33667             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33668             if(loader){
33669                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33670                 return;
33671             }
33672         }
33673         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33674     },
33675     
33676     /**
33677      * Returns true if this node is currently loading
33678      * @return {Boolean}
33679      */
33680     isLoading : function(){
33681         return this.loading;  
33682     },
33683     
33684     loadComplete : function(deep, anim, callback){
33685         this.loading = false;
33686         this.loaded = true;
33687         this.ui.afterLoad(this);
33688         this.fireEvent("load", this);
33689         this.expand(deep, anim, callback);
33690     },
33691     
33692     /**
33693      * Returns true if this node has been loaded
33694      * @return {Boolean}
33695      */
33696     isLoaded : function(){
33697         return this.loaded;
33698     },
33699     
33700     hasChildNodes : function(){
33701         if(!this.isLeaf() && !this.loaded){
33702             return true;
33703         }else{
33704             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33705         }
33706     },
33707
33708     /**
33709      * Trigger a reload for this node
33710      * @param {Function} callback
33711      */
33712     reload : function(callback){
33713         this.collapse(false, false);
33714         while(this.firstChild){
33715             this.removeChild(this.firstChild);
33716         }
33717         this.childrenRendered = false;
33718         this.loaded = false;
33719         if(this.isHiddenRoot()){
33720             this.expanded = false;
33721         }
33722         this.expand(false, false, callback);
33723     }
33724 });/*
33725  * Based on:
33726  * Ext JS Library 1.1.1
33727  * Copyright(c) 2006-2007, Ext JS, LLC.
33728  *
33729  * Originally Released Under LGPL - original licence link has changed is not relivant.
33730  *
33731  * Fork - LGPL
33732  * <script type="text/javascript">
33733  */
33734  
33735 /**
33736  * @class Roo.tree.TreeNodeUI
33737  * @constructor
33738  * @param {Object} node The node to render
33739  * The TreeNode UI implementation is separate from the
33740  * tree implementation. Unless you are customizing the tree UI,
33741  * you should never have to use this directly.
33742  */
33743 Roo.tree.TreeNodeUI = function(node){
33744     this.node = node;
33745     this.rendered = false;
33746     this.animating = false;
33747     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33748 };
33749
33750 Roo.tree.TreeNodeUI.prototype = {
33751     removeChild : function(node){
33752         if(this.rendered){
33753             this.ctNode.removeChild(node.ui.getEl());
33754         }
33755     },
33756
33757     beforeLoad : function(){
33758          this.addClass("x-tree-node-loading");
33759     },
33760
33761     afterLoad : function(){
33762          this.removeClass("x-tree-node-loading");
33763     },
33764
33765     onTextChange : function(node, text, oldText){
33766         if(this.rendered){
33767             this.textNode.innerHTML = text;
33768         }
33769     },
33770
33771     onDisableChange : function(node, state){
33772         this.disabled = state;
33773         if(state){
33774             this.addClass("x-tree-node-disabled");
33775         }else{
33776             this.removeClass("x-tree-node-disabled");
33777         }
33778     },
33779
33780     onSelectedChange : function(state){
33781         if(state){
33782             this.focus();
33783             this.addClass("x-tree-selected");
33784         }else{
33785             //this.blur();
33786             this.removeClass("x-tree-selected");
33787         }
33788     },
33789
33790     onMove : function(tree, node, oldParent, newParent, index, refNode){
33791         this.childIndent = null;
33792         if(this.rendered){
33793             var targetNode = newParent.ui.getContainer();
33794             if(!targetNode){//target not rendered
33795                 this.holder = document.createElement("div");
33796                 this.holder.appendChild(this.wrap);
33797                 return;
33798             }
33799             var insertBefore = refNode ? refNode.ui.getEl() : null;
33800             if(insertBefore){
33801                 targetNode.insertBefore(this.wrap, insertBefore);
33802             }else{
33803                 targetNode.appendChild(this.wrap);
33804             }
33805             this.node.renderIndent(true);
33806         }
33807     },
33808
33809     addClass : function(cls){
33810         if(this.elNode){
33811             Roo.fly(this.elNode).addClass(cls);
33812         }
33813     },
33814
33815     removeClass : function(cls){
33816         if(this.elNode){
33817             Roo.fly(this.elNode).removeClass(cls);
33818         }
33819     },
33820
33821     remove : function(){
33822         if(this.rendered){
33823             this.holder = document.createElement("div");
33824             this.holder.appendChild(this.wrap);
33825         }
33826     },
33827
33828     fireEvent : function(){
33829         return this.node.fireEvent.apply(this.node, arguments);
33830     },
33831
33832     initEvents : function(){
33833         this.node.on("move", this.onMove, this);
33834         var E = Roo.EventManager;
33835         var a = this.anchor;
33836
33837         var el = Roo.fly(a, '_treeui');
33838
33839         if(Roo.isOpera){ // opera render bug ignores the CSS
33840             el.setStyle("text-decoration", "none");
33841         }
33842
33843         el.on("click", this.onClick, this);
33844         el.on("dblclick", this.onDblClick, this);
33845
33846         if(this.checkbox){
33847             Roo.EventManager.on(this.checkbox,
33848                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33849         }
33850
33851         el.on("contextmenu", this.onContextMenu, this);
33852
33853         var icon = Roo.fly(this.iconNode);
33854         icon.on("click", this.onClick, this);
33855         icon.on("dblclick", this.onDblClick, this);
33856         icon.on("contextmenu", this.onContextMenu, this);
33857         E.on(this.ecNode, "click", this.ecClick, this, true);
33858
33859         if(this.node.disabled){
33860             this.addClass("x-tree-node-disabled");
33861         }
33862         if(this.node.hidden){
33863             this.addClass("x-tree-node-disabled");
33864         }
33865         var ot = this.node.getOwnerTree();
33866         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33867         if(dd && (!this.node.isRoot || ot.rootVisible)){
33868             Roo.dd.Registry.register(this.elNode, {
33869                 node: this.node,
33870                 handles: this.getDDHandles(),
33871                 isHandle: false
33872             });
33873         }
33874     },
33875
33876     getDDHandles : function(){
33877         return [this.iconNode, this.textNode];
33878     },
33879
33880     hide : function(){
33881         if(this.rendered){
33882             this.wrap.style.display = "none";
33883         }
33884     },
33885
33886     show : function(){
33887         if(this.rendered){
33888             this.wrap.style.display = "";
33889         }
33890     },
33891
33892     onContextMenu : function(e){
33893         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33894             e.preventDefault();
33895             this.focus();
33896             this.fireEvent("contextmenu", this.node, e);
33897         }
33898     },
33899
33900     onClick : function(e){
33901         if(this.dropping){
33902             e.stopEvent();
33903             return;
33904         }
33905         if(this.fireEvent("beforeclick", this.node, e) !== false){
33906             if(!this.disabled && this.node.attributes.href){
33907                 this.fireEvent("click", this.node, e);
33908                 return;
33909             }
33910             e.preventDefault();
33911             if(this.disabled){
33912                 return;
33913             }
33914
33915             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33916                 this.node.toggle();
33917             }
33918
33919             this.fireEvent("click", this.node, e);
33920         }else{
33921             e.stopEvent();
33922         }
33923     },
33924
33925     onDblClick : function(e){
33926         e.preventDefault();
33927         if(this.disabled){
33928             return;
33929         }
33930         if(this.checkbox){
33931             this.toggleCheck();
33932         }
33933         if(!this.animating && this.node.hasChildNodes()){
33934             this.node.toggle();
33935         }
33936         this.fireEvent("dblclick", this.node, e);
33937     },
33938
33939     onCheckChange : function(){
33940         var checked = this.checkbox.checked;
33941         this.node.attributes.checked = checked;
33942         this.fireEvent('checkchange', this.node, checked);
33943     },
33944
33945     ecClick : function(e){
33946         if(!this.animating && this.node.hasChildNodes()){
33947             this.node.toggle();
33948         }
33949     },
33950
33951     startDrop : function(){
33952         this.dropping = true;
33953     },
33954
33955     // delayed drop so the click event doesn't get fired on a drop
33956     endDrop : function(){
33957        setTimeout(function(){
33958            this.dropping = false;
33959        }.createDelegate(this), 50);
33960     },
33961
33962     expand : function(){
33963         this.updateExpandIcon();
33964         this.ctNode.style.display = "";
33965     },
33966
33967     focus : function(){
33968         if(!this.node.preventHScroll){
33969             try{this.anchor.focus();
33970             }catch(e){}
33971         }else if(!Roo.isIE){
33972             try{
33973                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33974                 var l = noscroll.scrollLeft;
33975                 this.anchor.focus();
33976                 noscroll.scrollLeft = l;
33977             }catch(e){}
33978         }
33979     },
33980
33981     toggleCheck : function(value){
33982         var cb = this.checkbox;
33983         if(cb){
33984             cb.checked = (value === undefined ? !cb.checked : value);
33985         }
33986     },
33987
33988     blur : function(){
33989         try{
33990             this.anchor.blur();
33991         }catch(e){}
33992     },
33993
33994     animExpand : function(callback){
33995         var ct = Roo.get(this.ctNode);
33996         ct.stopFx();
33997         if(!this.node.hasChildNodes()){
33998             this.updateExpandIcon();
33999             this.ctNode.style.display = "";
34000             Roo.callback(callback);
34001             return;
34002         }
34003         this.animating = true;
34004         this.updateExpandIcon();
34005
34006         ct.slideIn('t', {
34007            callback : function(){
34008                this.animating = false;
34009                Roo.callback(callback);
34010             },
34011             scope: this,
34012             duration: this.node.ownerTree.duration || .25
34013         });
34014     },
34015
34016     highlight : function(){
34017         var tree = this.node.getOwnerTree();
34018         Roo.fly(this.wrap).highlight(
34019             tree.hlColor || "C3DAF9",
34020             {endColor: tree.hlBaseColor}
34021         );
34022     },
34023
34024     collapse : function(){
34025         this.updateExpandIcon();
34026         this.ctNode.style.display = "none";
34027     },
34028
34029     animCollapse : function(callback){
34030         var ct = Roo.get(this.ctNode);
34031         ct.enableDisplayMode('block');
34032         ct.stopFx();
34033
34034         this.animating = true;
34035         this.updateExpandIcon();
34036
34037         ct.slideOut('t', {
34038             callback : function(){
34039                this.animating = false;
34040                Roo.callback(callback);
34041             },
34042             scope: this,
34043             duration: this.node.ownerTree.duration || .25
34044         });
34045     },
34046
34047     getContainer : function(){
34048         return this.ctNode;
34049     },
34050
34051     getEl : function(){
34052         return this.wrap;
34053     },
34054
34055     appendDDGhost : function(ghostNode){
34056         ghostNode.appendChild(this.elNode.cloneNode(true));
34057     },
34058
34059     getDDRepairXY : function(){
34060         return Roo.lib.Dom.getXY(this.iconNode);
34061     },
34062
34063     onRender : function(){
34064         this.render();
34065     },
34066
34067     render : function(bulkRender){
34068         var n = this.node, a = n.attributes;
34069         var targetNode = n.parentNode ?
34070               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34071
34072         if(!this.rendered){
34073             this.rendered = true;
34074
34075             this.renderElements(n, a, targetNode, bulkRender);
34076
34077             if(a.qtip){
34078                if(this.textNode.setAttributeNS){
34079                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34080                    if(a.qtipTitle){
34081                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34082                    }
34083                }else{
34084                    this.textNode.setAttribute("ext:qtip", a.qtip);
34085                    if(a.qtipTitle){
34086                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34087                    }
34088                }
34089             }else if(a.qtipCfg){
34090                 a.qtipCfg.target = Roo.id(this.textNode);
34091                 Roo.QuickTips.register(a.qtipCfg);
34092             }
34093             this.initEvents();
34094             if(!this.node.expanded){
34095                 this.updateExpandIcon();
34096             }
34097         }else{
34098             if(bulkRender === true) {
34099                 targetNode.appendChild(this.wrap);
34100             }
34101         }
34102     },
34103
34104     renderElements : function(n, a, targetNode, bulkRender)
34105     {
34106         // add some indent caching, this helps performance when rendering a large tree
34107         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34108         var t = n.getOwnerTree();
34109         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34110         if (typeof(n.attributes.html) != 'undefined') {
34111             txt = n.attributes.html;
34112         }
34113         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34114         var cb = typeof a.checked == 'boolean';
34115         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34116         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34117             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34118             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34119             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34120             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34121             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34122              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34123                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34124             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34125             "</li>"];
34126
34127         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34128             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34129                                 n.nextSibling.ui.getEl(), buf.join(""));
34130         }else{
34131             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34132         }
34133
34134         this.elNode = this.wrap.childNodes[0];
34135         this.ctNode = this.wrap.childNodes[1];
34136         var cs = this.elNode.childNodes;
34137         this.indentNode = cs[0];
34138         this.ecNode = cs[1];
34139         this.iconNode = cs[2];
34140         var index = 3;
34141         if(cb){
34142             this.checkbox = cs[3];
34143             index++;
34144         }
34145         this.anchor = cs[index];
34146         this.textNode = cs[index].firstChild;
34147     },
34148
34149     getAnchor : function(){
34150         return this.anchor;
34151     },
34152
34153     getTextEl : function(){
34154         return this.textNode;
34155     },
34156
34157     getIconEl : function(){
34158         return this.iconNode;
34159     },
34160
34161     isChecked : function(){
34162         return this.checkbox ? this.checkbox.checked : false;
34163     },
34164
34165     updateExpandIcon : function(){
34166         if(this.rendered){
34167             var n = this.node, c1, c2;
34168             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34169             var hasChild = n.hasChildNodes();
34170             if(hasChild){
34171                 if(n.expanded){
34172                     cls += "-minus";
34173                     c1 = "x-tree-node-collapsed";
34174                     c2 = "x-tree-node-expanded";
34175                 }else{
34176                     cls += "-plus";
34177                     c1 = "x-tree-node-expanded";
34178                     c2 = "x-tree-node-collapsed";
34179                 }
34180                 if(this.wasLeaf){
34181                     this.removeClass("x-tree-node-leaf");
34182                     this.wasLeaf = false;
34183                 }
34184                 if(this.c1 != c1 || this.c2 != c2){
34185                     Roo.fly(this.elNode).replaceClass(c1, c2);
34186                     this.c1 = c1; this.c2 = c2;
34187                 }
34188             }else{
34189                 // this changes non-leafs into leafs if they have no children.
34190                 // it's not very rational behaviour..
34191                 
34192                 if(!this.wasLeaf && this.node.leaf){
34193                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34194                     delete this.c1;
34195                     delete this.c2;
34196                     this.wasLeaf = true;
34197                 }
34198             }
34199             var ecc = "x-tree-ec-icon "+cls;
34200             if(this.ecc != ecc){
34201                 this.ecNode.className = ecc;
34202                 this.ecc = ecc;
34203             }
34204         }
34205     },
34206
34207     getChildIndent : function(){
34208         if(!this.childIndent){
34209             var buf = [];
34210             var p = this.node;
34211             while(p){
34212                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34213                     if(!p.isLast()) {
34214                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34215                     } else {
34216                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34217                     }
34218                 }
34219                 p = p.parentNode;
34220             }
34221             this.childIndent = buf.join("");
34222         }
34223         return this.childIndent;
34224     },
34225
34226     renderIndent : function(){
34227         if(this.rendered){
34228             var indent = "";
34229             var p = this.node.parentNode;
34230             if(p){
34231                 indent = p.ui.getChildIndent();
34232             }
34233             if(this.indentMarkup != indent){ // don't rerender if not required
34234                 this.indentNode.innerHTML = indent;
34235                 this.indentMarkup = indent;
34236             }
34237             this.updateExpandIcon();
34238         }
34239     }
34240 };
34241
34242 Roo.tree.RootTreeNodeUI = function(){
34243     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34244 };
34245 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34246     render : function(){
34247         if(!this.rendered){
34248             var targetNode = this.node.ownerTree.innerCt.dom;
34249             this.node.expanded = true;
34250             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34251             this.wrap = this.ctNode = targetNode.firstChild;
34252         }
34253     },
34254     collapse : function(){
34255     },
34256     expand : function(){
34257     }
34258 });/*
34259  * Based on:
34260  * Ext JS Library 1.1.1
34261  * Copyright(c) 2006-2007, Ext JS, LLC.
34262  *
34263  * Originally Released Under LGPL - original licence link has changed is not relivant.
34264  *
34265  * Fork - LGPL
34266  * <script type="text/javascript">
34267  */
34268 /**
34269  * @class Roo.tree.TreeLoader
34270  * @extends Roo.util.Observable
34271  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34272  * nodes from a specified URL. The response must be a javascript Array definition
34273  * who's elements are node definition objects. eg:
34274  * <pre><code>
34275 {  success : true,
34276    data :      [
34277    
34278     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34279     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34280     ]
34281 }
34282
34283
34284 </code></pre>
34285  * <br><br>
34286  * The old style respose with just an array is still supported, but not recommended.
34287  * <br><br>
34288  *
34289  * A server request is sent, and child nodes are loaded only when a node is expanded.
34290  * The loading node's id is passed to the server under the parameter name "node" to
34291  * enable the server to produce the correct child nodes.
34292  * <br><br>
34293  * To pass extra parameters, an event handler may be attached to the "beforeload"
34294  * event, and the parameters specified in the TreeLoader's baseParams property:
34295  * <pre><code>
34296     myTreeLoader.on("beforeload", function(treeLoader, node) {
34297         this.baseParams.category = node.attributes.category;
34298     }, this);
34299 </code></pre><
34300  * This would pass an HTTP parameter called "category" to the server containing
34301  * the value of the Node's "category" attribute.
34302  * @constructor
34303  * Creates a new Treeloader.
34304  * @param {Object} config A config object containing config properties.
34305  */
34306 Roo.tree.TreeLoader = function(config){
34307     this.baseParams = {};
34308     this.requestMethod = "POST";
34309     Roo.apply(this, config);
34310
34311     this.addEvents({
34312     
34313         /**
34314          * @event beforeload
34315          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34316          * @param {Object} This TreeLoader object.
34317          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34318          * @param {Object} callback The callback function specified in the {@link #load} call.
34319          */
34320         beforeload : true,
34321         /**
34322          * @event load
34323          * Fires when the node has been successfuly loaded.
34324          * @param {Object} This TreeLoader object.
34325          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34326          * @param {Object} response The response object containing the data from the server.
34327          */
34328         load : true,
34329         /**
34330          * @event loadexception
34331          * Fires if the network request failed.
34332          * @param {Object} This TreeLoader object.
34333          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34334          * @param {Object} response The response object containing the data from the server.
34335          */
34336         loadexception : true,
34337         /**
34338          * @event create
34339          * Fires before a node is created, enabling you to return custom Node types 
34340          * @param {Object} This TreeLoader object.
34341          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34342          */
34343         create : true
34344     });
34345
34346     Roo.tree.TreeLoader.superclass.constructor.call(this);
34347 };
34348
34349 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34350     /**
34351     * @cfg {String} dataUrl The URL from which to request a Json string which
34352     * specifies an array of node definition object representing the child nodes
34353     * to be loaded.
34354     */
34355     /**
34356     * @cfg {String} requestMethod either GET or POST
34357     * defaults to POST (due to BC)
34358     * to be loaded.
34359     */
34360     /**
34361     * @cfg {Object} baseParams (optional) An object containing properties which
34362     * specify HTTP parameters to be passed to each request for child nodes.
34363     */
34364     /**
34365     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34366     * created by this loader. If the attributes sent by the server have an attribute in this object,
34367     * they take priority.
34368     */
34369     /**
34370     * @cfg {Object} uiProviders (optional) An object containing properties which
34371     * 
34372     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34373     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34374     * <i>uiProvider</i> attribute of a returned child node is a string rather
34375     * than a reference to a TreeNodeUI implementation, this that string value
34376     * is used as a property name in the uiProviders object. You can define the provider named
34377     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34378     */
34379     uiProviders : {},
34380
34381     /**
34382     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34383     * child nodes before loading.
34384     */
34385     clearOnLoad : true,
34386
34387     /**
34388     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34389     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34390     * Grid query { data : [ .....] }
34391     */
34392     
34393     root : false,
34394      /**
34395     * @cfg {String} queryParam (optional) 
34396     * Name of the query as it will be passed on the querystring (defaults to 'node')
34397     * eg. the request will be ?node=[id]
34398     */
34399     
34400     
34401     queryParam: false,
34402     
34403     /**
34404      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34405      * This is called automatically when a node is expanded, but may be used to reload
34406      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34407      * @param {Roo.tree.TreeNode} node
34408      * @param {Function} callback
34409      */
34410     load : function(node, callback){
34411         if(this.clearOnLoad){
34412             while(node.firstChild){
34413                 node.removeChild(node.firstChild);
34414             }
34415         }
34416         if(node.attributes.children){ // preloaded json children
34417             var cs = node.attributes.children;
34418             for(var i = 0, len = cs.length; i < len; i++){
34419                 node.appendChild(this.createNode(cs[i]));
34420             }
34421             if(typeof callback == "function"){
34422                 callback();
34423             }
34424         }else if(this.dataUrl){
34425             this.requestData(node, callback);
34426         }
34427     },
34428
34429     getParams: function(node){
34430         var buf = [], bp = this.baseParams;
34431         for(var key in bp){
34432             if(typeof bp[key] != "function"){
34433                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34434             }
34435         }
34436         var n = this.queryParam === false ? 'node' : this.queryParam;
34437         buf.push(n + "=", encodeURIComponent(node.id));
34438         return buf.join("");
34439     },
34440
34441     requestData : function(node, callback){
34442         if(this.fireEvent("beforeload", this, node, callback) !== false){
34443             this.transId = Roo.Ajax.request({
34444                 method:this.requestMethod,
34445                 url: this.dataUrl||this.url,
34446                 success: this.handleResponse,
34447                 failure: this.handleFailure,
34448                 scope: this,
34449                 argument: {callback: callback, node: node},
34450                 params: this.getParams(node)
34451             });
34452         }else{
34453             // if the load is cancelled, make sure we notify
34454             // the node that we are done
34455             if(typeof callback == "function"){
34456                 callback();
34457             }
34458         }
34459     },
34460
34461     isLoading : function(){
34462         return this.transId ? true : false;
34463     },
34464
34465     abort : function(){
34466         if(this.isLoading()){
34467             Roo.Ajax.abort(this.transId);
34468         }
34469     },
34470
34471     // private
34472     createNode : function(attr)
34473     {
34474         // apply baseAttrs, nice idea Corey!
34475         if(this.baseAttrs){
34476             Roo.applyIf(attr, this.baseAttrs);
34477         }
34478         if(this.applyLoader !== false){
34479             attr.loader = this;
34480         }
34481         // uiProvider = depreciated..
34482         
34483         if(typeof(attr.uiProvider) == 'string'){
34484            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34485                 /**  eval:var:attr */ eval(attr.uiProvider);
34486         }
34487         if(typeof(this.uiProviders['default']) != 'undefined') {
34488             attr.uiProvider = this.uiProviders['default'];
34489         }
34490         
34491         this.fireEvent('create', this, attr);
34492         
34493         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34494         return(attr.leaf ?
34495                         new Roo.tree.TreeNode(attr) :
34496                         new Roo.tree.AsyncTreeNode(attr));
34497     },
34498
34499     processResponse : function(response, node, callback)
34500     {
34501         var json = response.responseText;
34502         try {
34503             
34504             var o = Roo.decode(json);
34505             
34506             if (this.root === false && typeof(o.success) != undefined) {
34507                 this.root = 'data'; // the default behaviour for list like data..
34508                 }
34509                 
34510             if (this.root !== false &&  !o.success) {
34511                 // it's a failure condition.
34512                 var a = response.argument;
34513                 this.fireEvent("loadexception", this, a.node, response);
34514                 Roo.log("Load failed - should have a handler really");
34515                 return;
34516             }
34517             
34518             
34519             
34520             if (this.root !== false) {
34521                  o = o[this.root];
34522             }
34523             
34524             for(var i = 0, len = o.length; i < len; i++){
34525                 var n = this.createNode(o[i]);
34526                 if(n){
34527                     node.appendChild(n);
34528                 }
34529             }
34530             if(typeof callback == "function"){
34531                 callback(this, node);
34532             }
34533         }catch(e){
34534             this.handleFailure(response);
34535         }
34536     },
34537
34538     handleResponse : function(response){
34539         this.transId = false;
34540         var a = response.argument;
34541         this.processResponse(response, a.node, a.callback);
34542         this.fireEvent("load", this, a.node, response);
34543     },
34544
34545     handleFailure : function(response)
34546     {
34547         // should handle failure better..
34548         this.transId = false;
34549         var a = response.argument;
34550         this.fireEvent("loadexception", this, a.node, response);
34551         if(typeof a.callback == "function"){
34552             a.callback(this, a.node);
34553         }
34554     }
34555 });/*
34556  * Based on:
34557  * Ext JS Library 1.1.1
34558  * Copyright(c) 2006-2007, Ext JS, LLC.
34559  *
34560  * Originally Released Under LGPL - original licence link has changed is not relivant.
34561  *
34562  * Fork - LGPL
34563  * <script type="text/javascript">
34564  */
34565
34566 /**
34567 * @class Roo.tree.TreeFilter
34568 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34569 * @param {TreePanel} tree
34570 * @param {Object} config (optional)
34571  */
34572 Roo.tree.TreeFilter = function(tree, config){
34573     this.tree = tree;
34574     this.filtered = {};
34575     Roo.apply(this, config);
34576 };
34577
34578 Roo.tree.TreeFilter.prototype = {
34579     clearBlank:false,
34580     reverse:false,
34581     autoClear:false,
34582     remove:false,
34583
34584      /**
34585      * Filter the data by a specific attribute.
34586      * @param {String/RegExp} value Either string that the attribute value
34587      * should start with or a RegExp to test against the attribute
34588      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34589      * @param {TreeNode} startNode (optional) The node to start the filter at.
34590      */
34591     filter : function(value, attr, startNode){
34592         attr = attr || "text";
34593         var f;
34594         if(typeof value == "string"){
34595             var vlen = value.length;
34596             // auto clear empty filter
34597             if(vlen == 0 && this.clearBlank){
34598                 this.clear();
34599                 return;
34600             }
34601             value = value.toLowerCase();
34602             f = function(n){
34603                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34604             };
34605         }else if(value.exec){ // regex?
34606             f = function(n){
34607                 return value.test(n.attributes[attr]);
34608             };
34609         }else{
34610             throw 'Illegal filter type, must be string or regex';
34611         }
34612         this.filterBy(f, null, startNode);
34613         },
34614
34615     /**
34616      * Filter by a function. The passed function will be called with each
34617      * node in the tree (or from the startNode). If the function returns true, the node is kept
34618      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34619      * @param {Function} fn The filter function
34620      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34621      */
34622     filterBy : function(fn, scope, startNode){
34623         startNode = startNode || this.tree.root;
34624         if(this.autoClear){
34625             this.clear();
34626         }
34627         var af = this.filtered, rv = this.reverse;
34628         var f = function(n){
34629             if(n == startNode){
34630                 return true;
34631             }
34632             if(af[n.id]){
34633                 return false;
34634             }
34635             var m = fn.call(scope || n, n);
34636             if(!m || rv){
34637                 af[n.id] = n;
34638                 n.ui.hide();
34639                 return false;
34640             }
34641             return true;
34642         };
34643         startNode.cascade(f);
34644         if(this.remove){
34645            for(var id in af){
34646                if(typeof id != "function"){
34647                    var n = af[id];
34648                    if(n && n.parentNode){
34649                        n.parentNode.removeChild(n);
34650                    }
34651                }
34652            }
34653         }
34654     },
34655
34656     /**
34657      * Clears the current filter. Note: with the "remove" option
34658      * set a filter cannot be cleared.
34659      */
34660     clear : function(){
34661         var t = this.tree;
34662         var af = this.filtered;
34663         for(var id in af){
34664             if(typeof id != "function"){
34665                 var n = af[id];
34666                 if(n){
34667                     n.ui.show();
34668                 }
34669             }
34670         }
34671         this.filtered = {};
34672     }
34673 };
34674 /*
34675  * Based on:
34676  * Ext JS Library 1.1.1
34677  * Copyright(c) 2006-2007, Ext JS, LLC.
34678  *
34679  * Originally Released Under LGPL - original licence link has changed is not relivant.
34680  *
34681  * Fork - LGPL
34682  * <script type="text/javascript">
34683  */
34684  
34685
34686 /**
34687  * @class Roo.tree.TreeSorter
34688  * Provides sorting of nodes in a TreePanel
34689  * 
34690  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34691  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34692  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34693  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34694  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34695  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34696  * @constructor
34697  * @param {TreePanel} tree
34698  * @param {Object} config
34699  */
34700 Roo.tree.TreeSorter = function(tree, config){
34701     Roo.apply(this, config);
34702     tree.on("beforechildrenrendered", this.doSort, this);
34703     tree.on("append", this.updateSort, this);
34704     tree.on("insert", this.updateSort, this);
34705     
34706     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34707     var p = this.property || "text";
34708     var sortType = this.sortType;
34709     var fs = this.folderSort;
34710     var cs = this.caseSensitive === true;
34711     var leafAttr = this.leafAttr || 'leaf';
34712
34713     this.sortFn = function(n1, n2){
34714         if(fs){
34715             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34716                 return 1;
34717             }
34718             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34719                 return -1;
34720             }
34721         }
34722         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34723         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34724         if(v1 < v2){
34725                         return dsc ? +1 : -1;
34726                 }else if(v1 > v2){
34727                         return dsc ? -1 : +1;
34728         }else{
34729                 return 0;
34730         }
34731     };
34732 };
34733
34734 Roo.tree.TreeSorter.prototype = {
34735     doSort : function(node){
34736         node.sort(this.sortFn);
34737     },
34738     
34739     compareNodes : function(n1, n2){
34740         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34741     },
34742     
34743     updateSort : function(tree, node){
34744         if(node.childrenRendered){
34745             this.doSort.defer(1, this, [node]);
34746         }
34747     }
34748 };/*
34749  * Based on:
34750  * Ext JS Library 1.1.1
34751  * Copyright(c) 2006-2007, Ext JS, LLC.
34752  *
34753  * Originally Released Under LGPL - original licence link has changed is not relivant.
34754  *
34755  * Fork - LGPL
34756  * <script type="text/javascript">
34757  */
34758
34759 if(Roo.dd.DropZone){
34760     
34761 Roo.tree.TreeDropZone = function(tree, config){
34762     this.allowParentInsert = false;
34763     this.allowContainerDrop = false;
34764     this.appendOnly = false;
34765     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34766     this.tree = tree;
34767     this.lastInsertClass = "x-tree-no-status";
34768     this.dragOverData = {};
34769 };
34770
34771 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34772     ddGroup : "TreeDD",
34773     scroll:  true,
34774     
34775     expandDelay : 1000,
34776     
34777     expandNode : function(node){
34778         if(node.hasChildNodes() && !node.isExpanded()){
34779             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34780         }
34781     },
34782     
34783     queueExpand : function(node){
34784         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34785     },
34786     
34787     cancelExpand : function(){
34788         if(this.expandProcId){
34789             clearTimeout(this.expandProcId);
34790             this.expandProcId = false;
34791         }
34792     },
34793     
34794     isValidDropPoint : function(n, pt, dd, e, data){
34795         if(!n || !data){ return false; }
34796         var targetNode = n.node;
34797         var dropNode = data.node;
34798         // default drop rules
34799         if(!(targetNode && targetNode.isTarget && pt)){
34800             return false;
34801         }
34802         if(pt == "append" && targetNode.allowChildren === false){
34803             return false;
34804         }
34805         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34806             return false;
34807         }
34808         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34809             return false;
34810         }
34811         // reuse the object
34812         var overEvent = this.dragOverData;
34813         overEvent.tree = this.tree;
34814         overEvent.target = targetNode;
34815         overEvent.data = data;
34816         overEvent.point = pt;
34817         overEvent.source = dd;
34818         overEvent.rawEvent = e;
34819         overEvent.dropNode = dropNode;
34820         overEvent.cancel = false;  
34821         var result = this.tree.fireEvent("nodedragover", overEvent);
34822         return overEvent.cancel === false && result !== false;
34823     },
34824     
34825     getDropPoint : function(e, n, dd)
34826     {
34827         var tn = n.node;
34828         if(tn.isRoot){
34829             return tn.allowChildren !== false ? "append" : false; // always append for root
34830         }
34831         var dragEl = n.ddel;
34832         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34833         var y = Roo.lib.Event.getPageY(e);
34834         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34835         
34836         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34837         var noAppend = tn.allowChildren === false;
34838         if(this.appendOnly || tn.parentNode.allowChildren === false){
34839             return noAppend ? false : "append";
34840         }
34841         var noBelow = false;
34842         if(!this.allowParentInsert){
34843             noBelow = tn.hasChildNodes() && tn.isExpanded();
34844         }
34845         var q = (b - t) / (noAppend ? 2 : 3);
34846         if(y >= t && y < (t + q)){
34847             return "above";
34848         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34849             return "below";
34850         }else{
34851             return "append";
34852         }
34853     },
34854     
34855     onNodeEnter : function(n, dd, e, data)
34856     {
34857         this.cancelExpand();
34858     },
34859     
34860     onNodeOver : function(n, dd, e, data)
34861     {
34862        
34863         var pt = this.getDropPoint(e, n, dd);
34864         var node = n.node;
34865         
34866         // auto node expand check
34867         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34868             this.queueExpand(node);
34869         }else if(pt != "append"){
34870             this.cancelExpand();
34871         }
34872         
34873         // set the insert point style on the target node
34874         var returnCls = this.dropNotAllowed;
34875         if(this.isValidDropPoint(n, pt, dd, e, data)){
34876            if(pt){
34877                var el = n.ddel;
34878                var cls;
34879                if(pt == "above"){
34880                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34881                    cls = "x-tree-drag-insert-above";
34882                }else if(pt == "below"){
34883                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34884                    cls = "x-tree-drag-insert-below";
34885                }else{
34886                    returnCls = "x-tree-drop-ok-append";
34887                    cls = "x-tree-drag-append";
34888                }
34889                if(this.lastInsertClass != cls){
34890                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34891                    this.lastInsertClass = cls;
34892                }
34893            }
34894        }
34895        return returnCls;
34896     },
34897     
34898     onNodeOut : function(n, dd, e, data){
34899         
34900         this.cancelExpand();
34901         this.removeDropIndicators(n);
34902     },
34903     
34904     onNodeDrop : function(n, dd, e, data){
34905         var point = this.getDropPoint(e, n, dd);
34906         var targetNode = n.node;
34907         targetNode.ui.startDrop();
34908         if(!this.isValidDropPoint(n, point, dd, e, data)){
34909             targetNode.ui.endDrop();
34910             return false;
34911         }
34912         // first try to find the drop node
34913         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34914         var dropEvent = {
34915             tree : this.tree,
34916             target: targetNode,
34917             data: data,
34918             point: point,
34919             source: dd,
34920             rawEvent: e,
34921             dropNode: dropNode,
34922             cancel: !dropNode   
34923         };
34924         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34925         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34926             targetNode.ui.endDrop();
34927             return false;
34928         }
34929         // allow target changing
34930         targetNode = dropEvent.target;
34931         if(point == "append" && !targetNode.isExpanded()){
34932             targetNode.expand(false, null, function(){
34933                 this.completeDrop(dropEvent);
34934             }.createDelegate(this));
34935         }else{
34936             this.completeDrop(dropEvent);
34937         }
34938         return true;
34939     },
34940     
34941     completeDrop : function(de){
34942         var ns = de.dropNode, p = de.point, t = de.target;
34943         if(!(ns instanceof Array)){
34944             ns = [ns];
34945         }
34946         var n;
34947         for(var i = 0, len = ns.length; i < len; i++){
34948             n = ns[i];
34949             if(p == "above"){
34950                 t.parentNode.insertBefore(n, t);
34951             }else if(p == "below"){
34952                 t.parentNode.insertBefore(n, t.nextSibling);
34953             }else{
34954                 t.appendChild(n);
34955             }
34956         }
34957         n.ui.focus();
34958         if(this.tree.hlDrop){
34959             n.ui.highlight();
34960         }
34961         t.ui.endDrop();
34962         this.tree.fireEvent("nodedrop", de);
34963     },
34964     
34965     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34966         if(this.tree.hlDrop){
34967             dropNode.ui.focus();
34968             dropNode.ui.highlight();
34969         }
34970         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34971     },
34972     
34973     getTree : function(){
34974         return this.tree;
34975     },
34976     
34977     removeDropIndicators : function(n){
34978         if(n && n.ddel){
34979             var el = n.ddel;
34980             Roo.fly(el).removeClass([
34981                     "x-tree-drag-insert-above",
34982                     "x-tree-drag-insert-below",
34983                     "x-tree-drag-append"]);
34984             this.lastInsertClass = "_noclass";
34985         }
34986     },
34987     
34988     beforeDragDrop : function(target, e, id){
34989         this.cancelExpand();
34990         return true;
34991     },
34992     
34993     afterRepair : function(data){
34994         if(data && Roo.enableFx){
34995             data.node.ui.highlight();
34996         }
34997         this.hideProxy();
34998     } 
34999     
35000 });
35001
35002 }
35003 /*
35004  * Based on:
35005  * Ext JS Library 1.1.1
35006  * Copyright(c) 2006-2007, Ext JS, LLC.
35007  *
35008  * Originally Released Under LGPL - original licence link has changed is not relivant.
35009  *
35010  * Fork - LGPL
35011  * <script type="text/javascript">
35012  */
35013  
35014
35015 if(Roo.dd.DragZone){
35016 Roo.tree.TreeDragZone = function(tree, config){
35017     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35018     this.tree = tree;
35019 };
35020
35021 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35022     ddGroup : "TreeDD",
35023    
35024     onBeforeDrag : function(data, e){
35025         var n = data.node;
35026         return n && n.draggable && !n.disabled;
35027     },
35028      
35029     
35030     onInitDrag : function(e){
35031         var data = this.dragData;
35032         this.tree.getSelectionModel().select(data.node);
35033         this.proxy.update("");
35034         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35035         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35036     },
35037     
35038     getRepairXY : function(e, data){
35039         return data.node.ui.getDDRepairXY();
35040     },
35041     
35042     onEndDrag : function(data, e){
35043         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35044         
35045         
35046     },
35047     
35048     onValidDrop : function(dd, e, id){
35049         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35050         this.hideProxy();
35051     },
35052     
35053     beforeInvalidDrop : function(e, id){
35054         // this scrolls the original position back into view
35055         var sm = this.tree.getSelectionModel();
35056         sm.clearSelections();
35057         sm.select(this.dragData.node);
35058     }
35059 });
35060 }/*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070 /**
35071  * @class Roo.tree.TreeEditor
35072  * @extends Roo.Editor
35073  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35074  * as the editor field.
35075  * @constructor
35076  * @param {Object} config (used to be the tree panel.)
35077  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35078  * 
35079  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35080  * @cfg {Roo.form.TextField|Object} field The field configuration
35081  *
35082  * 
35083  */
35084 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35085     var tree = config;
35086     var field;
35087     if (oldconfig) { // old style..
35088         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35089     } else {
35090         // new style..
35091         tree = config.tree;
35092         config.field = config.field  || {};
35093         config.field.xtype = 'TextField';
35094         field = Roo.factory(config.field, Roo.form);
35095     }
35096     config = config || {};
35097     
35098     
35099     this.addEvents({
35100         /**
35101          * @event beforenodeedit
35102          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35103          * false from the handler of this event.
35104          * @param {Editor} this
35105          * @param {Roo.tree.Node} node 
35106          */
35107         "beforenodeedit" : true
35108     });
35109     
35110     //Roo.log(config);
35111     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35112
35113     this.tree = tree;
35114
35115     tree.on('beforeclick', this.beforeNodeClick, this);
35116     tree.getTreeEl().on('mousedown', this.hide, this);
35117     this.on('complete', this.updateNode, this);
35118     this.on('beforestartedit', this.fitToTree, this);
35119     this.on('startedit', this.bindScroll, this, {delay:10});
35120     this.on('specialkey', this.onSpecialKey, this);
35121 };
35122
35123 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35124     /**
35125      * @cfg {String} alignment
35126      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35127      */
35128     alignment: "l-l",
35129     // inherit
35130     autoSize: false,
35131     /**
35132      * @cfg {Boolean} hideEl
35133      * True to hide the bound element while the editor is displayed (defaults to false)
35134      */
35135     hideEl : false,
35136     /**
35137      * @cfg {String} cls
35138      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35139      */
35140     cls: "x-small-editor x-tree-editor",
35141     /**
35142      * @cfg {Boolean} shim
35143      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35144      */
35145     shim:false,
35146     // inherit
35147     shadow:"frame",
35148     /**
35149      * @cfg {Number} maxWidth
35150      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35151      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35152      * scroll and client offsets into account prior to each edit.
35153      */
35154     maxWidth: 250,
35155
35156     editDelay : 350,
35157
35158     // private
35159     fitToTree : function(ed, el){
35160         var td = this.tree.getTreeEl().dom, nd = el.dom;
35161         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35162             td.scrollLeft = nd.offsetLeft;
35163         }
35164         var w = Math.min(
35165                 this.maxWidth,
35166                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35167         this.setSize(w, '');
35168         
35169         return this.fireEvent('beforenodeedit', this, this.editNode);
35170         
35171     },
35172
35173     // private
35174     triggerEdit : function(node){
35175         this.completeEdit();
35176         this.editNode = node;
35177         this.startEdit(node.ui.textNode, node.text);
35178     },
35179
35180     // private
35181     bindScroll : function(){
35182         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35183     },
35184
35185     // private
35186     beforeNodeClick : function(node, e){
35187         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35188         this.lastClick = new Date();
35189         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35190             e.stopEvent();
35191             this.triggerEdit(node);
35192             return false;
35193         }
35194         return true;
35195     },
35196
35197     // private
35198     updateNode : function(ed, value){
35199         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35200         this.editNode.setText(value);
35201     },
35202
35203     // private
35204     onHide : function(){
35205         Roo.tree.TreeEditor.superclass.onHide.call(this);
35206         if(this.editNode){
35207             this.editNode.ui.focus();
35208         }
35209     },
35210
35211     // private
35212     onSpecialKey : function(field, e){
35213         var k = e.getKey();
35214         if(k == e.ESC){
35215             e.stopEvent();
35216             this.cancelEdit();
35217         }else if(k == e.ENTER && !e.hasModifier()){
35218             e.stopEvent();
35219             this.completeEdit();
35220         }
35221     }
35222 });//<Script type="text/javascript">
35223 /*
35224  * Based on:
35225  * Ext JS Library 1.1.1
35226  * Copyright(c) 2006-2007, Ext JS, LLC.
35227  *
35228  * Originally Released Under LGPL - original licence link has changed is not relivant.
35229  *
35230  * Fork - LGPL
35231  * <script type="text/javascript">
35232  */
35233  
35234 /**
35235  * Not documented??? - probably should be...
35236  */
35237
35238 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35239     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35240     
35241     renderElements : function(n, a, targetNode, bulkRender){
35242         //consel.log("renderElements?");
35243         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35244
35245         var t = n.getOwnerTree();
35246         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35247         
35248         var cols = t.columns;
35249         var bw = t.borderWidth;
35250         var c = cols[0];
35251         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35252          var cb = typeof a.checked == "boolean";
35253         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35254         var colcls = 'x-t-' + tid + '-c0';
35255         var buf = [
35256             '<li class="x-tree-node">',
35257             
35258                 
35259                 '<div class="x-tree-node-el ', a.cls,'">',
35260                     // extran...
35261                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35262                 
35263                 
35264                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35265                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35266                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35267                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35268                            (a.iconCls ? ' '+a.iconCls : ''),
35269                            '" unselectable="on" />',
35270                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35271                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35272                              
35273                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35274                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35275                             '<span unselectable="on" qtip="' + tx + '">',
35276                              tx,
35277                              '</span></a>' ,
35278                     '</div>',
35279                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35280                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35281                  ];
35282         for(var i = 1, len = cols.length; i < len; i++){
35283             c = cols[i];
35284             colcls = 'x-t-' + tid + '-c' +i;
35285             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35286             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35287                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35288                       "</div>");
35289          }
35290          
35291          buf.push(
35292             '</a>',
35293             '<div class="x-clear"></div></div>',
35294             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35295             "</li>");
35296         
35297         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35298             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35299                                 n.nextSibling.ui.getEl(), buf.join(""));
35300         }else{
35301             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35302         }
35303         var el = this.wrap.firstChild;
35304         this.elRow = el;
35305         this.elNode = el.firstChild;
35306         this.ranchor = el.childNodes[1];
35307         this.ctNode = this.wrap.childNodes[1];
35308         var cs = el.firstChild.childNodes;
35309         this.indentNode = cs[0];
35310         this.ecNode = cs[1];
35311         this.iconNode = cs[2];
35312         var index = 3;
35313         if(cb){
35314             this.checkbox = cs[3];
35315             index++;
35316         }
35317         this.anchor = cs[index];
35318         
35319         this.textNode = cs[index].firstChild;
35320         
35321         //el.on("click", this.onClick, this);
35322         //el.on("dblclick", this.onDblClick, this);
35323         
35324         
35325        // console.log(this);
35326     },
35327     initEvents : function(){
35328         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35329         
35330             
35331         var a = this.ranchor;
35332
35333         var el = Roo.get(a);
35334
35335         if(Roo.isOpera){ // opera render bug ignores the CSS
35336             el.setStyle("text-decoration", "none");
35337         }
35338
35339         el.on("click", this.onClick, this);
35340         el.on("dblclick", this.onDblClick, this);
35341         el.on("contextmenu", this.onContextMenu, this);
35342         
35343     },
35344     
35345     /*onSelectedChange : function(state){
35346         if(state){
35347             this.focus();
35348             this.addClass("x-tree-selected");
35349         }else{
35350             //this.blur();
35351             this.removeClass("x-tree-selected");
35352         }
35353     },*/
35354     addClass : function(cls){
35355         if(this.elRow){
35356             Roo.fly(this.elRow).addClass(cls);
35357         }
35358         
35359     },
35360     
35361     
35362     removeClass : function(cls){
35363         if(this.elRow){
35364             Roo.fly(this.elRow).removeClass(cls);
35365         }
35366     }
35367
35368     
35369     
35370 });//<Script type="text/javascript">
35371
35372 /*
35373  * Based on:
35374  * Ext JS Library 1.1.1
35375  * Copyright(c) 2006-2007, Ext JS, LLC.
35376  *
35377  * Originally Released Under LGPL - original licence link has changed is not relivant.
35378  *
35379  * Fork - LGPL
35380  * <script type="text/javascript">
35381  */
35382  
35383
35384 /**
35385  * @class Roo.tree.ColumnTree
35386  * @extends Roo.data.TreePanel
35387  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35388  * @cfg {int} borderWidth  compined right/left border allowance
35389  * @constructor
35390  * @param {String/HTMLElement/Element} el The container element
35391  * @param {Object} config
35392  */
35393 Roo.tree.ColumnTree =  function(el, config)
35394 {
35395    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35396    this.addEvents({
35397         /**
35398         * @event resize
35399         * Fire this event on a container when it resizes
35400         * @param {int} w Width
35401         * @param {int} h Height
35402         */
35403        "resize" : true
35404     });
35405     this.on('resize', this.onResize, this);
35406 };
35407
35408 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35409     //lines:false,
35410     
35411     
35412     borderWidth: Roo.isBorderBox ? 0 : 2, 
35413     headEls : false,
35414     
35415     render : function(){
35416         // add the header.....
35417        
35418         Roo.tree.ColumnTree.superclass.render.apply(this);
35419         
35420         this.el.addClass('x-column-tree');
35421         
35422         this.headers = this.el.createChild(
35423             {cls:'x-tree-headers'},this.innerCt.dom);
35424    
35425         var cols = this.columns, c;
35426         var totalWidth = 0;
35427         this.headEls = [];
35428         var  len = cols.length;
35429         for(var i = 0; i < len; i++){
35430              c = cols[i];
35431              totalWidth += c.width;
35432             this.headEls.push(this.headers.createChild({
35433                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35434                  cn: {
35435                      cls:'x-tree-hd-text',
35436                      html: c.header
35437                  },
35438                  style:'width:'+(c.width-this.borderWidth)+'px;'
35439              }));
35440         }
35441         this.headers.createChild({cls:'x-clear'});
35442         // prevent floats from wrapping when clipped
35443         this.headers.setWidth(totalWidth);
35444         //this.innerCt.setWidth(totalWidth);
35445         this.innerCt.setStyle({ overflow: 'auto' });
35446         this.onResize(this.width, this.height);
35447              
35448         
35449     },
35450     onResize : function(w,h)
35451     {
35452         this.height = h;
35453         this.width = w;
35454         // resize cols..
35455         this.innerCt.setWidth(this.width);
35456         this.innerCt.setHeight(this.height-20);
35457         
35458         // headers...
35459         var cols = this.columns, c;
35460         var totalWidth = 0;
35461         var expEl = false;
35462         var len = cols.length;
35463         for(var i = 0; i < len; i++){
35464             c = cols[i];
35465             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35466                 // it's the expander..
35467                 expEl  = this.headEls[i];
35468                 continue;
35469             }
35470             totalWidth += c.width;
35471             
35472         }
35473         if (expEl) {
35474             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35475         }
35476         this.headers.setWidth(w-20);
35477
35478         
35479         
35480         
35481     }
35482 });
35483 /*
35484  * Based on:
35485  * Ext JS Library 1.1.1
35486  * Copyright(c) 2006-2007, Ext JS, LLC.
35487  *
35488  * Originally Released Under LGPL - original licence link has changed is not relivant.
35489  *
35490  * Fork - LGPL
35491  * <script type="text/javascript">
35492  */
35493  
35494 /**
35495  * @class Roo.menu.Menu
35496  * @extends Roo.util.Observable
35497  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35498  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35499  * @constructor
35500  * Creates a new Menu
35501  * @param {Object} config Configuration options
35502  */
35503 Roo.menu.Menu = function(config){
35504     Roo.apply(this, config);
35505     this.id = this.id || Roo.id();
35506     this.addEvents({
35507         /**
35508          * @event beforeshow
35509          * Fires before this menu is displayed
35510          * @param {Roo.menu.Menu} this
35511          */
35512         beforeshow : true,
35513         /**
35514          * @event beforehide
35515          * Fires before this menu is hidden
35516          * @param {Roo.menu.Menu} this
35517          */
35518         beforehide : true,
35519         /**
35520          * @event show
35521          * Fires after this menu is displayed
35522          * @param {Roo.menu.Menu} this
35523          */
35524         show : true,
35525         /**
35526          * @event hide
35527          * Fires after this menu is hidden
35528          * @param {Roo.menu.Menu} this
35529          */
35530         hide : true,
35531         /**
35532          * @event click
35533          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35534          * @param {Roo.menu.Menu} this
35535          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35536          * @param {Roo.EventObject} e
35537          */
35538         click : true,
35539         /**
35540          * @event mouseover
35541          * Fires when the mouse is hovering over this menu
35542          * @param {Roo.menu.Menu} this
35543          * @param {Roo.EventObject} e
35544          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35545          */
35546         mouseover : true,
35547         /**
35548          * @event mouseout
35549          * Fires when the mouse exits this menu
35550          * @param {Roo.menu.Menu} this
35551          * @param {Roo.EventObject} e
35552          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35553          */
35554         mouseout : true,
35555         /**
35556          * @event itemclick
35557          * Fires when a menu item contained in this menu is clicked
35558          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35559          * @param {Roo.EventObject} e
35560          */
35561         itemclick: true
35562     });
35563     if (this.registerMenu) {
35564         Roo.menu.MenuMgr.register(this);
35565     }
35566     
35567     var mis = this.items;
35568     this.items = new Roo.util.MixedCollection();
35569     if(mis){
35570         this.add.apply(this, mis);
35571     }
35572 };
35573
35574 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35575     /**
35576      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35577      */
35578     minWidth : 120,
35579     /**
35580      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35581      * for bottom-right shadow (defaults to "sides")
35582      */
35583     shadow : "sides",
35584     /**
35585      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35586      * this menu (defaults to "tl-tr?")
35587      */
35588     subMenuAlign : "tl-tr?",
35589     /**
35590      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35591      * relative to its element of origin (defaults to "tl-bl?")
35592      */
35593     defaultAlign : "tl-bl?",
35594     /**
35595      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35596      */
35597     allowOtherMenus : false,
35598     /**
35599      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35600      */
35601     registerMenu : true,
35602
35603     hidden:true,
35604
35605     // private
35606     render : function(){
35607         if(this.el){
35608             return;
35609         }
35610         var el = this.el = new Roo.Layer({
35611             cls: "x-menu",
35612             shadow:this.shadow,
35613             constrain: false,
35614             parentEl: this.parentEl || document.body,
35615             zindex:15000
35616         });
35617
35618         this.keyNav = new Roo.menu.MenuNav(this);
35619
35620         if(this.plain){
35621             el.addClass("x-menu-plain");
35622         }
35623         if(this.cls){
35624             el.addClass(this.cls);
35625         }
35626         // generic focus element
35627         this.focusEl = el.createChild({
35628             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35629         });
35630         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35631         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35632         
35633         ul.on("mouseover", this.onMouseOver, this);
35634         ul.on("mouseout", this.onMouseOut, this);
35635         this.items.each(function(item){
35636             if (item.hidden) {
35637                 return;
35638             }
35639             
35640             var li = document.createElement("li");
35641             li.className = "x-menu-list-item";
35642             ul.dom.appendChild(li);
35643             item.render(li, this);
35644         }, this);
35645         this.ul = ul;
35646         this.autoWidth();
35647     },
35648
35649     // private
35650     autoWidth : function(){
35651         var el = this.el, ul = this.ul;
35652         if(!el){
35653             return;
35654         }
35655         var w = this.width;
35656         if(w){
35657             el.setWidth(w);
35658         }else if(Roo.isIE){
35659             el.setWidth(this.minWidth);
35660             var t = el.dom.offsetWidth; // force recalc
35661             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35662         }
35663     },
35664
35665     // private
35666     delayAutoWidth : function(){
35667         if(this.rendered){
35668             if(!this.awTask){
35669                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35670             }
35671             this.awTask.delay(20);
35672         }
35673     },
35674
35675     // private
35676     findTargetItem : function(e){
35677         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35678         if(t && t.menuItemId){
35679             return this.items.get(t.menuItemId);
35680         }
35681     },
35682
35683     // private
35684     onClick : function(e){
35685         Roo.log("menu.onClick");
35686         var t = this.findTargetItem(e);
35687         if(!t){
35688             return;
35689         }
35690         Roo.log(e);
35691         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35692             if(t == this.activeItem && t.shouldDeactivate(e)){
35693                 this.activeItem.deactivate();
35694                 delete this.activeItem;
35695                 return;
35696             }
35697             if(t.canActivate){
35698                 this.setActiveItem(t, true);
35699             }
35700             return;
35701             
35702             
35703         }
35704         
35705         t.onClick(e);
35706         this.fireEvent("click", this, t, e);
35707     },
35708
35709     // private
35710     setActiveItem : function(item, autoExpand){
35711         if(item != this.activeItem){
35712             if(this.activeItem){
35713                 this.activeItem.deactivate();
35714             }
35715             this.activeItem = item;
35716             item.activate(autoExpand);
35717         }else if(autoExpand){
35718             item.expandMenu();
35719         }
35720     },
35721
35722     // private
35723     tryActivate : function(start, step){
35724         var items = this.items;
35725         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35726             var item = items.get(i);
35727             if(!item.disabled && item.canActivate){
35728                 this.setActiveItem(item, false);
35729                 return item;
35730             }
35731         }
35732         return false;
35733     },
35734
35735     // private
35736     onMouseOver : function(e){
35737         var t;
35738         if(t = this.findTargetItem(e)){
35739             if(t.canActivate && !t.disabled){
35740                 this.setActiveItem(t, true);
35741             }
35742         }
35743         this.fireEvent("mouseover", this, e, t);
35744     },
35745
35746     // private
35747     onMouseOut : function(e){
35748         var t;
35749         if(t = this.findTargetItem(e)){
35750             if(t == this.activeItem && t.shouldDeactivate(e)){
35751                 this.activeItem.deactivate();
35752                 delete this.activeItem;
35753             }
35754         }
35755         this.fireEvent("mouseout", this, e, t);
35756     },
35757
35758     /**
35759      * Read-only.  Returns true if the menu is currently displayed, else false.
35760      * @type Boolean
35761      */
35762     isVisible : function(){
35763         return this.el && !this.hidden;
35764     },
35765
35766     /**
35767      * Displays this menu relative to another element
35768      * @param {String/HTMLElement/Roo.Element} element The element to align to
35769      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35770      * the element (defaults to this.defaultAlign)
35771      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35772      */
35773     show : function(el, pos, parentMenu){
35774         this.parentMenu = parentMenu;
35775         if(!this.el){
35776             this.render();
35777         }
35778         this.fireEvent("beforeshow", this);
35779         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35780     },
35781
35782     /**
35783      * Displays this menu at a specific xy position
35784      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35785      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35786      */
35787     showAt : function(xy, parentMenu, /* private: */_e){
35788         this.parentMenu = parentMenu;
35789         if(!this.el){
35790             this.render();
35791         }
35792         if(_e !== false){
35793             this.fireEvent("beforeshow", this);
35794             xy = this.el.adjustForConstraints(xy);
35795         }
35796         this.el.setXY(xy);
35797         this.el.show();
35798         this.hidden = false;
35799         this.focus();
35800         this.fireEvent("show", this);
35801     },
35802
35803     focus : function(){
35804         if(!this.hidden){
35805             this.doFocus.defer(50, this);
35806         }
35807     },
35808
35809     doFocus : function(){
35810         if(!this.hidden){
35811             this.focusEl.focus();
35812         }
35813     },
35814
35815     /**
35816      * Hides this menu and optionally all parent menus
35817      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35818      */
35819     hide : function(deep){
35820         if(this.el && this.isVisible()){
35821             this.fireEvent("beforehide", this);
35822             if(this.activeItem){
35823                 this.activeItem.deactivate();
35824                 this.activeItem = null;
35825             }
35826             this.el.hide();
35827             this.hidden = true;
35828             this.fireEvent("hide", this);
35829         }
35830         if(deep === true && this.parentMenu){
35831             this.parentMenu.hide(true);
35832         }
35833     },
35834
35835     /**
35836      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35837      * Any of the following are valid:
35838      * <ul>
35839      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35840      * <li>An HTMLElement object which will be converted to a menu item</li>
35841      * <li>A menu item config object that will be created as a new menu item</li>
35842      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35843      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35844      * </ul>
35845      * Usage:
35846      * <pre><code>
35847 // Create the menu
35848 var menu = new Roo.menu.Menu();
35849
35850 // Create a menu item to add by reference
35851 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35852
35853 // Add a bunch of items at once using different methods.
35854 // Only the last item added will be returned.
35855 var item = menu.add(
35856     menuItem,                // add existing item by ref
35857     'Dynamic Item',          // new TextItem
35858     '-',                     // new separator
35859     { text: 'Config Item' }  // new item by config
35860 );
35861 </code></pre>
35862      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35863      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35864      */
35865     add : function(){
35866         var a = arguments, l = a.length, item;
35867         for(var i = 0; i < l; i++){
35868             var el = a[i];
35869             if ((typeof(el) == "object") && el.xtype && el.xns) {
35870                 el = Roo.factory(el, Roo.menu);
35871             }
35872             
35873             if(el.render){ // some kind of Item
35874                 item = this.addItem(el);
35875             }else if(typeof el == "string"){ // string
35876                 if(el == "separator" || el == "-"){
35877                     item = this.addSeparator();
35878                 }else{
35879                     item = this.addText(el);
35880                 }
35881             }else if(el.tagName || el.el){ // element
35882                 item = this.addElement(el);
35883             }else if(typeof el == "object"){ // must be menu item config?
35884                 item = this.addMenuItem(el);
35885             }
35886         }
35887         return item;
35888     },
35889
35890     /**
35891      * Returns this menu's underlying {@link Roo.Element} object
35892      * @return {Roo.Element} The element
35893      */
35894     getEl : function(){
35895         if(!this.el){
35896             this.render();
35897         }
35898         return this.el;
35899     },
35900
35901     /**
35902      * Adds a separator bar to the menu
35903      * @return {Roo.menu.Item} The menu item that was added
35904      */
35905     addSeparator : function(){
35906         return this.addItem(new Roo.menu.Separator());
35907     },
35908
35909     /**
35910      * Adds an {@link Roo.Element} object to the menu
35911      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35912      * @return {Roo.menu.Item} The menu item that was added
35913      */
35914     addElement : function(el){
35915         return this.addItem(new Roo.menu.BaseItem(el));
35916     },
35917
35918     /**
35919      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35920      * @param {Roo.menu.Item} item The menu item to add
35921      * @return {Roo.menu.Item} The menu item that was added
35922      */
35923     addItem : function(item){
35924         this.items.add(item);
35925         if(this.ul){
35926             var li = document.createElement("li");
35927             li.className = "x-menu-list-item";
35928             this.ul.dom.appendChild(li);
35929             item.render(li, this);
35930             this.delayAutoWidth();
35931         }
35932         return item;
35933     },
35934
35935     /**
35936      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35937      * @param {Object} config A MenuItem config object
35938      * @return {Roo.menu.Item} The menu item that was added
35939      */
35940     addMenuItem : function(config){
35941         if(!(config instanceof Roo.menu.Item)){
35942             if(typeof config.checked == "boolean"){ // must be check menu item config?
35943                 config = new Roo.menu.CheckItem(config);
35944             }else{
35945                 config = new Roo.menu.Item(config);
35946             }
35947         }
35948         return this.addItem(config);
35949     },
35950
35951     /**
35952      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35953      * @param {String} text The text to display in the menu item
35954      * @return {Roo.menu.Item} The menu item that was added
35955      */
35956     addText : function(text){
35957         return this.addItem(new Roo.menu.TextItem({ text : text }));
35958     },
35959
35960     /**
35961      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35962      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35963      * @param {Roo.menu.Item} item The menu item to add
35964      * @return {Roo.menu.Item} The menu item that was added
35965      */
35966     insert : function(index, item){
35967         this.items.insert(index, item);
35968         if(this.ul){
35969             var li = document.createElement("li");
35970             li.className = "x-menu-list-item";
35971             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35972             item.render(li, this);
35973             this.delayAutoWidth();
35974         }
35975         return item;
35976     },
35977
35978     /**
35979      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35980      * @param {Roo.menu.Item} item The menu item to remove
35981      */
35982     remove : function(item){
35983         this.items.removeKey(item.id);
35984         item.destroy();
35985     },
35986
35987     /**
35988      * Removes and destroys all items in the menu
35989      */
35990     removeAll : function(){
35991         var f;
35992         while(f = this.items.first()){
35993             this.remove(f);
35994         }
35995     }
35996 });
35997
35998 // MenuNav is a private utility class used internally by the Menu
35999 Roo.menu.MenuNav = function(menu){
36000     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36001     this.scope = this.menu = menu;
36002 };
36003
36004 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36005     doRelay : function(e, h){
36006         var k = e.getKey();
36007         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36008             this.menu.tryActivate(0, 1);
36009             return false;
36010         }
36011         return h.call(this.scope || this, e, this.menu);
36012     },
36013
36014     up : function(e, m){
36015         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36016             m.tryActivate(m.items.length-1, -1);
36017         }
36018     },
36019
36020     down : function(e, m){
36021         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36022             m.tryActivate(0, 1);
36023         }
36024     },
36025
36026     right : function(e, m){
36027         if(m.activeItem){
36028             m.activeItem.expandMenu(true);
36029         }
36030     },
36031
36032     left : function(e, m){
36033         m.hide();
36034         if(m.parentMenu && m.parentMenu.activeItem){
36035             m.parentMenu.activeItem.activate();
36036         }
36037     },
36038
36039     enter : function(e, m){
36040         if(m.activeItem){
36041             e.stopPropagation();
36042             m.activeItem.onClick(e);
36043             m.fireEvent("click", this, m.activeItem);
36044             return true;
36045         }
36046     }
36047 });/*
36048  * Based on:
36049  * Ext JS Library 1.1.1
36050  * Copyright(c) 2006-2007, Ext JS, LLC.
36051  *
36052  * Originally Released Under LGPL - original licence link has changed is not relivant.
36053  *
36054  * Fork - LGPL
36055  * <script type="text/javascript">
36056  */
36057  
36058 /**
36059  * @class Roo.menu.MenuMgr
36060  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36061  * @singleton
36062  */
36063 Roo.menu.MenuMgr = function(){
36064    var menus, active, groups = {}, attached = false, lastShow = new Date();
36065
36066    // private - called when first menu is created
36067    function init(){
36068        menus = {};
36069        active = new Roo.util.MixedCollection();
36070        Roo.get(document).addKeyListener(27, function(){
36071            if(active.length > 0){
36072                hideAll();
36073            }
36074        });
36075    }
36076
36077    // private
36078    function hideAll(){
36079        if(active && active.length > 0){
36080            var c = active.clone();
36081            c.each(function(m){
36082                m.hide();
36083            });
36084        }
36085    }
36086
36087    // private
36088    function onHide(m){
36089        active.remove(m);
36090        if(active.length < 1){
36091            Roo.get(document).un("mousedown", onMouseDown);
36092            attached = false;
36093        }
36094    }
36095
36096    // private
36097    function onShow(m){
36098        var last = active.last();
36099        lastShow = new Date();
36100        active.add(m);
36101        if(!attached){
36102            Roo.get(document).on("mousedown", onMouseDown);
36103            attached = true;
36104        }
36105        if(m.parentMenu){
36106           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36107           m.parentMenu.activeChild = m;
36108        }else if(last && last.isVisible()){
36109           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36110        }
36111    }
36112
36113    // private
36114    function onBeforeHide(m){
36115        if(m.activeChild){
36116            m.activeChild.hide();
36117        }
36118        if(m.autoHideTimer){
36119            clearTimeout(m.autoHideTimer);
36120            delete m.autoHideTimer;
36121        }
36122    }
36123
36124    // private
36125    function onBeforeShow(m){
36126        var pm = m.parentMenu;
36127        if(!pm && !m.allowOtherMenus){
36128            hideAll();
36129        }else if(pm && pm.activeChild && active != m){
36130            pm.activeChild.hide();
36131        }
36132    }
36133
36134    // private
36135    function onMouseDown(e){
36136        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36137            hideAll();
36138        }
36139    }
36140
36141    // private
36142    function onBeforeCheck(mi, state){
36143        if(state){
36144            var g = groups[mi.group];
36145            for(var i = 0, l = g.length; i < l; i++){
36146                if(g[i] != mi){
36147                    g[i].setChecked(false);
36148                }
36149            }
36150        }
36151    }
36152
36153    return {
36154
36155        /**
36156         * Hides all menus that are currently visible
36157         */
36158        hideAll : function(){
36159             hideAll();  
36160        },
36161
36162        // private
36163        register : function(menu){
36164            if(!menus){
36165                init();
36166            }
36167            menus[menu.id] = menu;
36168            menu.on("beforehide", onBeforeHide);
36169            menu.on("hide", onHide);
36170            menu.on("beforeshow", onBeforeShow);
36171            menu.on("show", onShow);
36172            var g = menu.group;
36173            if(g && menu.events["checkchange"]){
36174                if(!groups[g]){
36175                    groups[g] = [];
36176                }
36177                groups[g].push(menu);
36178                menu.on("checkchange", onCheck);
36179            }
36180        },
36181
36182         /**
36183          * Returns a {@link Roo.menu.Menu} object
36184          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36185          * be used to generate and return a new Menu instance.
36186          */
36187        get : function(menu){
36188            if(typeof menu == "string"){ // menu id
36189                return menus[menu];
36190            }else if(menu.events){  // menu instance
36191                return menu;
36192            }else if(typeof menu.length == 'number'){ // array of menu items?
36193                return new Roo.menu.Menu({items:menu});
36194            }else{ // otherwise, must be a config
36195                return new Roo.menu.Menu(menu);
36196            }
36197        },
36198
36199        // private
36200        unregister : function(menu){
36201            delete menus[menu.id];
36202            menu.un("beforehide", onBeforeHide);
36203            menu.un("hide", onHide);
36204            menu.un("beforeshow", onBeforeShow);
36205            menu.un("show", onShow);
36206            var g = menu.group;
36207            if(g && menu.events["checkchange"]){
36208                groups[g].remove(menu);
36209                menu.un("checkchange", onCheck);
36210            }
36211        },
36212
36213        // private
36214        registerCheckable : function(menuItem){
36215            var g = menuItem.group;
36216            if(g){
36217                if(!groups[g]){
36218                    groups[g] = [];
36219                }
36220                groups[g].push(menuItem);
36221                menuItem.on("beforecheckchange", onBeforeCheck);
36222            }
36223        },
36224
36225        // private
36226        unregisterCheckable : function(menuItem){
36227            var g = menuItem.group;
36228            if(g){
36229                groups[g].remove(menuItem);
36230                menuItem.un("beforecheckchange", onBeforeCheck);
36231            }
36232        }
36233    };
36234 }();/*
36235  * Based on:
36236  * Ext JS Library 1.1.1
36237  * Copyright(c) 2006-2007, Ext JS, LLC.
36238  *
36239  * Originally Released Under LGPL - original licence link has changed is not relivant.
36240  *
36241  * Fork - LGPL
36242  * <script type="text/javascript">
36243  */
36244  
36245
36246 /**
36247  * @class Roo.menu.BaseItem
36248  * @extends Roo.Component
36249  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36250  * management and base configuration options shared by all menu components.
36251  * @constructor
36252  * Creates a new BaseItem
36253  * @param {Object} config Configuration options
36254  */
36255 Roo.menu.BaseItem = function(config){
36256     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36257
36258     this.addEvents({
36259         /**
36260          * @event click
36261          * Fires when this item is clicked
36262          * @param {Roo.menu.BaseItem} this
36263          * @param {Roo.EventObject} e
36264          */
36265         click: true,
36266         /**
36267          * @event activate
36268          * Fires when this item is activated
36269          * @param {Roo.menu.BaseItem} this
36270          */
36271         activate : true,
36272         /**
36273          * @event deactivate
36274          * Fires when this item is deactivated
36275          * @param {Roo.menu.BaseItem} this
36276          */
36277         deactivate : true
36278     });
36279
36280     if(this.handler){
36281         this.on("click", this.handler, this.scope, true);
36282     }
36283 };
36284
36285 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36286     /**
36287      * @cfg {Function} handler
36288      * A function that will handle the click event of this menu item (defaults to undefined)
36289      */
36290     /**
36291      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36292      */
36293     canActivate : false,
36294     
36295      /**
36296      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36297      */
36298     hidden: false,
36299     
36300     /**
36301      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36302      */
36303     activeClass : "x-menu-item-active",
36304     /**
36305      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36306      */
36307     hideOnClick : true,
36308     /**
36309      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36310      */
36311     hideDelay : 100,
36312
36313     // private
36314     ctype: "Roo.menu.BaseItem",
36315
36316     // private
36317     actionMode : "container",
36318
36319     // private
36320     render : function(container, parentMenu){
36321         this.parentMenu = parentMenu;
36322         Roo.menu.BaseItem.superclass.render.call(this, container);
36323         this.container.menuItemId = this.id;
36324     },
36325
36326     // private
36327     onRender : function(container, position){
36328         this.el = Roo.get(this.el);
36329         container.dom.appendChild(this.el.dom);
36330     },
36331
36332     // private
36333     onClick : function(e){
36334         if(!this.disabled && this.fireEvent("click", this, e) !== false
36335                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36336             this.handleClick(e);
36337         }else{
36338             e.stopEvent();
36339         }
36340     },
36341
36342     // private
36343     activate : function(){
36344         if(this.disabled){
36345             return false;
36346         }
36347         var li = this.container;
36348         li.addClass(this.activeClass);
36349         this.region = li.getRegion().adjust(2, 2, -2, -2);
36350         this.fireEvent("activate", this);
36351         return true;
36352     },
36353
36354     // private
36355     deactivate : function(){
36356         this.container.removeClass(this.activeClass);
36357         this.fireEvent("deactivate", this);
36358     },
36359
36360     // private
36361     shouldDeactivate : function(e){
36362         return !this.region || !this.region.contains(e.getPoint());
36363     },
36364
36365     // private
36366     handleClick : function(e){
36367         if(this.hideOnClick){
36368             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36369         }
36370     },
36371
36372     // private
36373     expandMenu : function(autoActivate){
36374         // do nothing
36375     },
36376
36377     // private
36378     hideMenu : function(){
36379         // do nothing
36380     }
36381 });/*
36382  * Based on:
36383  * Ext JS Library 1.1.1
36384  * Copyright(c) 2006-2007, Ext JS, LLC.
36385  *
36386  * Originally Released Under LGPL - original licence link has changed is not relivant.
36387  *
36388  * Fork - LGPL
36389  * <script type="text/javascript">
36390  */
36391  
36392 /**
36393  * @class Roo.menu.Adapter
36394  * @extends Roo.menu.BaseItem
36395  * 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.
36396  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36397  * @constructor
36398  * Creates a new Adapter
36399  * @param {Object} config Configuration options
36400  */
36401 Roo.menu.Adapter = function(component, config){
36402     Roo.menu.Adapter.superclass.constructor.call(this, config);
36403     this.component = component;
36404 };
36405 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36406     // private
36407     canActivate : true,
36408
36409     // private
36410     onRender : function(container, position){
36411         this.component.render(container);
36412         this.el = this.component.getEl();
36413     },
36414
36415     // private
36416     activate : function(){
36417         if(this.disabled){
36418             return false;
36419         }
36420         this.component.focus();
36421         this.fireEvent("activate", this);
36422         return true;
36423     },
36424
36425     // private
36426     deactivate : function(){
36427         this.fireEvent("deactivate", this);
36428     },
36429
36430     // private
36431     disable : function(){
36432         this.component.disable();
36433         Roo.menu.Adapter.superclass.disable.call(this);
36434     },
36435
36436     // private
36437     enable : function(){
36438         this.component.enable();
36439         Roo.menu.Adapter.superclass.enable.call(this);
36440     }
36441 });/*
36442  * Based on:
36443  * Ext JS Library 1.1.1
36444  * Copyright(c) 2006-2007, Ext JS, LLC.
36445  *
36446  * Originally Released Under LGPL - original licence link has changed is not relivant.
36447  *
36448  * Fork - LGPL
36449  * <script type="text/javascript">
36450  */
36451
36452 /**
36453  * @class Roo.menu.TextItem
36454  * @extends Roo.menu.BaseItem
36455  * Adds a static text string to a menu, usually used as either a heading or group separator.
36456  * Note: old style constructor with text is still supported.
36457  * 
36458  * @constructor
36459  * Creates a new TextItem
36460  * @param {Object} cfg Configuration
36461  */
36462 Roo.menu.TextItem = function(cfg){
36463     if (typeof(cfg) == 'string') {
36464         this.text = cfg;
36465     } else {
36466         Roo.apply(this,cfg);
36467     }
36468     
36469     Roo.menu.TextItem.superclass.constructor.call(this);
36470 };
36471
36472 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36473     /**
36474      * @cfg {Boolean} text Text to show on item.
36475      */
36476     text : '',
36477     
36478     /**
36479      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36480      */
36481     hideOnClick : false,
36482     /**
36483      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36484      */
36485     itemCls : "x-menu-text",
36486
36487     // private
36488     onRender : function(){
36489         var s = document.createElement("span");
36490         s.className = this.itemCls;
36491         s.innerHTML = this.text;
36492         this.el = s;
36493         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36494     }
36495 });/*
36496  * Based on:
36497  * Ext JS Library 1.1.1
36498  * Copyright(c) 2006-2007, Ext JS, LLC.
36499  *
36500  * Originally Released Under LGPL - original licence link has changed is not relivant.
36501  *
36502  * Fork - LGPL
36503  * <script type="text/javascript">
36504  */
36505
36506 /**
36507  * @class Roo.menu.Separator
36508  * @extends Roo.menu.BaseItem
36509  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36510  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36511  * @constructor
36512  * @param {Object} config Configuration options
36513  */
36514 Roo.menu.Separator = function(config){
36515     Roo.menu.Separator.superclass.constructor.call(this, config);
36516 };
36517
36518 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36519     /**
36520      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36521      */
36522     itemCls : "x-menu-sep",
36523     /**
36524      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36525      */
36526     hideOnClick : false,
36527
36528     // private
36529     onRender : function(li){
36530         var s = document.createElement("span");
36531         s.className = this.itemCls;
36532         s.innerHTML = "&#160;";
36533         this.el = s;
36534         li.addClass("x-menu-sep-li");
36535         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36536     }
36537 });/*
36538  * Based on:
36539  * Ext JS Library 1.1.1
36540  * Copyright(c) 2006-2007, Ext JS, LLC.
36541  *
36542  * Originally Released Under LGPL - original licence link has changed is not relivant.
36543  *
36544  * Fork - LGPL
36545  * <script type="text/javascript">
36546  */
36547 /**
36548  * @class Roo.menu.Item
36549  * @extends Roo.menu.BaseItem
36550  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36551  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36552  * activation and click handling.
36553  * @constructor
36554  * Creates a new Item
36555  * @param {Object} config Configuration options
36556  */
36557 Roo.menu.Item = function(config){
36558     Roo.menu.Item.superclass.constructor.call(this, config);
36559     if(this.menu){
36560         this.menu = Roo.menu.MenuMgr.get(this.menu);
36561     }
36562 };
36563 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36564     
36565     /**
36566      * @cfg {String} text
36567      * The text to show on the menu item.
36568      */
36569     text: '',
36570      /**
36571      * @cfg {String} HTML to render in menu
36572      * The text to show on the menu item (HTML version).
36573      */
36574     html: '',
36575     /**
36576      * @cfg {String} icon
36577      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36578      */
36579     icon: undefined,
36580     /**
36581      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36582      */
36583     itemCls : "x-menu-item",
36584     /**
36585      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36586      */
36587     canActivate : true,
36588     /**
36589      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36590      */
36591     showDelay: 200,
36592     // doc'd in BaseItem
36593     hideDelay: 200,
36594
36595     // private
36596     ctype: "Roo.menu.Item",
36597     
36598     // private
36599     onRender : function(container, position){
36600         var el = document.createElement("a");
36601         el.hideFocus = true;
36602         el.unselectable = "on";
36603         el.href = this.href || "#";
36604         if(this.hrefTarget){
36605             el.target = this.hrefTarget;
36606         }
36607         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36608         
36609         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36610         
36611         el.innerHTML = String.format(
36612                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36613                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36614         this.el = el;
36615         Roo.menu.Item.superclass.onRender.call(this, container, position);
36616     },
36617
36618     /**
36619      * Sets the text to display in this menu item
36620      * @param {String} text The text to display
36621      * @param {Boolean} isHTML true to indicate text is pure html.
36622      */
36623     setText : function(text, isHTML){
36624         if (isHTML) {
36625             this.html = text;
36626         } else {
36627             this.text = text;
36628             this.html = '';
36629         }
36630         if(this.rendered){
36631             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36632      
36633             this.el.update(String.format(
36634                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36635                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36636             this.parentMenu.autoWidth();
36637         }
36638     },
36639
36640     // private
36641     handleClick : function(e){
36642         if(!this.href){ // if no link defined, stop the event automatically
36643             e.stopEvent();
36644         }
36645         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36646     },
36647
36648     // private
36649     activate : function(autoExpand){
36650         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36651             this.focus();
36652             if(autoExpand){
36653                 this.expandMenu();
36654             }
36655         }
36656         return true;
36657     },
36658
36659     // private
36660     shouldDeactivate : function(e){
36661         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36662             if(this.menu && this.menu.isVisible()){
36663                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36664             }
36665             return true;
36666         }
36667         return false;
36668     },
36669
36670     // private
36671     deactivate : function(){
36672         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36673         this.hideMenu();
36674     },
36675
36676     // private
36677     expandMenu : function(autoActivate){
36678         if(!this.disabled && this.menu){
36679             clearTimeout(this.hideTimer);
36680             delete this.hideTimer;
36681             if(!this.menu.isVisible() && !this.showTimer){
36682                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36683             }else if (this.menu.isVisible() && autoActivate){
36684                 this.menu.tryActivate(0, 1);
36685             }
36686         }
36687     },
36688
36689     // private
36690     deferExpand : function(autoActivate){
36691         delete this.showTimer;
36692         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36693         if(autoActivate){
36694             this.menu.tryActivate(0, 1);
36695         }
36696     },
36697
36698     // private
36699     hideMenu : function(){
36700         clearTimeout(this.showTimer);
36701         delete this.showTimer;
36702         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36703             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36704         }
36705     },
36706
36707     // private
36708     deferHide : function(){
36709         delete this.hideTimer;
36710         this.menu.hide();
36711     }
36712 });/*
36713  * Based on:
36714  * Ext JS Library 1.1.1
36715  * Copyright(c) 2006-2007, Ext JS, LLC.
36716  *
36717  * Originally Released Under LGPL - original licence link has changed is not relivant.
36718  *
36719  * Fork - LGPL
36720  * <script type="text/javascript">
36721  */
36722  
36723 /**
36724  * @class Roo.menu.CheckItem
36725  * @extends Roo.menu.Item
36726  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36727  * @constructor
36728  * Creates a new CheckItem
36729  * @param {Object} config Configuration options
36730  */
36731 Roo.menu.CheckItem = function(config){
36732     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36733     this.addEvents({
36734         /**
36735          * @event beforecheckchange
36736          * Fires before the checked value is set, providing an opportunity to cancel if needed
36737          * @param {Roo.menu.CheckItem} this
36738          * @param {Boolean} checked The new checked value that will be set
36739          */
36740         "beforecheckchange" : true,
36741         /**
36742          * @event checkchange
36743          * Fires after the checked value has been set
36744          * @param {Roo.menu.CheckItem} this
36745          * @param {Boolean} checked The checked value that was set
36746          */
36747         "checkchange" : true
36748     });
36749     if(this.checkHandler){
36750         this.on('checkchange', this.checkHandler, this.scope);
36751     }
36752 };
36753 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36754     /**
36755      * @cfg {String} group
36756      * All check items with the same group name will automatically be grouped into a single-select
36757      * radio button group (defaults to '')
36758      */
36759     /**
36760      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36761      */
36762     itemCls : "x-menu-item x-menu-check-item",
36763     /**
36764      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36765      */
36766     groupClass : "x-menu-group-item",
36767
36768     /**
36769      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36770      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36771      * initialized with checked = true will be rendered as checked.
36772      */
36773     checked: false,
36774
36775     // private
36776     ctype: "Roo.menu.CheckItem",
36777
36778     // private
36779     onRender : function(c){
36780         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36781         if(this.group){
36782             this.el.addClass(this.groupClass);
36783         }
36784         Roo.menu.MenuMgr.registerCheckable(this);
36785         if(this.checked){
36786             this.checked = false;
36787             this.setChecked(true, true);
36788         }
36789     },
36790
36791     // private
36792     destroy : function(){
36793         if(this.rendered){
36794             Roo.menu.MenuMgr.unregisterCheckable(this);
36795         }
36796         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36797     },
36798
36799     /**
36800      * Set the checked state of this item
36801      * @param {Boolean} checked The new checked value
36802      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36803      */
36804     setChecked : function(state, suppressEvent){
36805         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36806             if(this.container){
36807                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36808             }
36809             this.checked = state;
36810             if(suppressEvent !== true){
36811                 this.fireEvent("checkchange", this, state);
36812             }
36813         }
36814     },
36815
36816     // private
36817     handleClick : function(e){
36818        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36819            this.setChecked(!this.checked);
36820        }
36821        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36822     }
36823 });/*
36824  * Based on:
36825  * Ext JS Library 1.1.1
36826  * Copyright(c) 2006-2007, Ext JS, LLC.
36827  *
36828  * Originally Released Under LGPL - original licence link has changed is not relivant.
36829  *
36830  * Fork - LGPL
36831  * <script type="text/javascript">
36832  */
36833  
36834 /**
36835  * @class Roo.menu.DateItem
36836  * @extends Roo.menu.Adapter
36837  * A menu item that wraps the {@link Roo.DatPicker} component.
36838  * @constructor
36839  * Creates a new DateItem
36840  * @param {Object} config Configuration options
36841  */
36842 Roo.menu.DateItem = function(config){
36843     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36844     /** The Roo.DatePicker object @type Roo.DatePicker */
36845     this.picker = this.component;
36846     this.addEvents({select: true});
36847     
36848     this.picker.on("render", function(picker){
36849         picker.getEl().swallowEvent("click");
36850         picker.container.addClass("x-menu-date-item");
36851     });
36852
36853     this.picker.on("select", this.onSelect, this);
36854 };
36855
36856 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36857     // private
36858     onSelect : function(picker, date){
36859         this.fireEvent("select", this, date, picker);
36860         Roo.menu.DateItem.superclass.handleClick.call(this);
36861     }
36862 });/*
36863  * Based on:
36864  * Ext JS Library 1.1.1
36865  * Copyright(c) 2006-2007, Ext JS, LLC.
36866  *
36867  * Originally Released Under LGPL - original licence link has changed is not relivant.
36868  *
36869  * Fork - LGPL
36870  * <script type="text/javascript">
36871  */
36872  
36873 /**
36874  * @class Roo.menu.ColorItem
36875  * @extends Roo.menu.Adapter
36876  * A menu item that wraps the {@link Roo.ColorPalette} component.
36877  * @constructor
36878  * Creates a new ColorItem
36879  * @param {Object} config Configuration options
36880  */
36881 Roo.menu.ColorItem = function(config){
36882     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36883     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36884     this.palette = this.component;
36885     this.relayEvents(this.palette, ["select"]);
36886     if(this.selectHandler){
36887         this.on('select', this.selectHandler, this.scope);
36888     }
36889 };
36890 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36891  * Based on:
36892  * Ext JS Library 1.1.1
36893  * Copyright(c) 2006-2007, Ext JS, LLC.
36894  *
36895  * Originally Released Under LGPL - original licence link has changed is not relivant.
36896  *
36897  * Fork - LGPL
36898  * <script type="text/javascript">
36899  */
36900  
36901
36902 /**
36903  * @class Roo.menu.DateMenu
36904  * @extends Roo.menu.Menu
36905  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36906  * @constructor
36907  * Creates a new DateMenu
36908  * @param {Object} config Configuration options
36909  */
36910 Roo.menu.DateMenu = function(config){
36911     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36912     this.plain = true;
36913     var di = new Roo.menu.DateItem(config);
36914     this.add(di);
36915     /**
36916      * The {@link Roo.DatePicker} instance for this DateMenu
36917      * @type DatePicker
36918      */
36919     this.picker = di.picker;
36920     /**
36921      * @event select
36922      * @param {DatePicker} picker
36923      * @param {Date} date
36924      */
36925     this.relayEvents(di, ["select"]);
36926     this.on('beforeshow', function(){
36927         if(this.picker){
36928             this.picker.hideMonthPicker(false);
36929         }
36930     }, this);
36931 };
36932 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36933     cls:'x-date-menu'
36934 });/*
36935  * Based on:
36936  * Ext JS Library 1.1.1
36937  * Copyright(c) 2006-2007, Ext JS, LLC.
36938  *
36939  * Originally Released Under LGPL - original licence link has changed is not relivant.
36940  *
36941  * Fork - LGPL
36942  * <script type="text/javascript">
36943  */
36944  
36945
36946 /**
36947  * @class Roo.menu.ColorMenu
36948  * @extends Roo.menu.Menu
36949  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36950  * @constructor
36951  * Creates a new ColorMenu
36952  * @param {Object} config Configuration options
36953  */
36954 Roo.menu.ColorMenu = function(config){
36955     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36956     this.plain = true;
36957     var ci = new Roo.menu.ColorItem(config);
36958     this.add(ci);
36959     /**
36960      * The {@link Roo.ColorPalette} instance for this ColorMenu
36961      * @type ColorPalette
36962      */
36963     this.palette = ci.palette;
36964     /**
36965      * @event select
36966      * @param {ColorPalette} palette
36967      * @param {String} color
36968      */
36969     this.relayEvents(ci, ["select"]);
36970 };
36971 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36972  * Based on:
36973  * Ext JS Library 1.1.1
36974  * Copyright(c) 2006-2007, Ext JS, LLC.
36975  *
36976  * Originally Released Under LGPL - original licence link has changed is not relivant.
36977  *
36978  * Fork - LGPL
36979  * <script type="text/javascript">
36980  */
36981  
36982 /**
36983  * @class Roo.form.Field
36984  * @extends Roo.BoxComponent
36985  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36986  * @constructor
36987  * Creates a new Field
36988  * @param {Object} config Configuration options
36989  */
36990 Roo.form.Field = function(config){
36991     Roo.form.Field.superclass.constructor.call(this, config);
36992 };
36993
36994 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36995     /**
36996      * @cfg {String} fieldLabel Label to use when rendering a form.
36997      */
36998        /**
36999      * @cfg {String} qtip Mouse over tip
37000      */
37001      
37002     /**
37003      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37004      */
37005     invalidClass : "x-form-invalid",
37006     /**
37007      * @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")
37008      */
37009     invalidText : "The value in this field is invalid",
37010     /**
37011      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37012      */
37013     focusClass : "x-form-focus",
37014     /**
37015      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37016       automatic validation (defaults to "keyup").
37017      */
37018     validationEvent : "keyup",
37019     /**
37020      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37021      */
37022     validateOnBlur : true,
37023     /**
37024      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37025      */
37026     validationDelay : 250,
37027     /**
37028      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37029      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37030      */
37031     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
37032     /**
37033      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37034      */
37035     fieldClass : "x-form-field",
37036     /**
37037      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37038      *<pre>
37039 Value         Description
37040 -----------   ----------------------------------------------------------------------
37041 qtip          Display a quick tip when the user hovers over the field
37042 title         Display a default browser title attribute popup
37043 under         Add a block div beneath the field containing the error text
37044 side          Add an error icon to the right of the field with a popup on hover
37045 [element id]  Add the error text directly to the innerHTML of the specified element
37046 </pre>
37047      */
37048     msgTarget : 'qtip',
37049     /**
37050      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37051      */
37052     msgFx : 'normal',
37053
37054     /**
37055      * @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.
37056      */
37057     readOnly : false,
37058
37059     /**
37060      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37061      */
37062     disabled : false,
37063
37064     /**
37065      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37066      */
37067     inputType : undefined,
37068     
37069     /**
37070      * @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).
37071          */
37072         tabIndex : undefined,
37073         
37074     // private
37075     isFormField : true,
37076
37077     // private
37078     hasFocus : false,
37079     /**
37080      * @property {Roo.Element} fieldEl
37081      * Element Containing the rendered Field (with label etc.)
37082      */
37083     /**
37084      * @cfg {Mixed} value A value to initialize this field with.
37085      */
37086     value : undefined,
37087
37088     /**
37089      * @cfg {String} name The field's HTML name attribute.
37090      */
37091     /**
37092      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37093      */
37094
37095         // private ??
37096         initComponent : function(){
37097         Roo.form.Field.superclass.initComponent.call(this);
37098         this.addEvents({
37099             /**
37100              * @event focus
37101              * Fires when this field receives input focus.
37102              * @param {Roo.form.Field} this
37103              */
37104             focus : true,
37105             /**
37106              * @event blur
37107              * Fires when this field loses input focus.
37108              * @param {Roo.form.Field} this
37109              */
37110             blur : true,
37111             /**
37112              * @event specialkey
37113              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37114              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37115              * @param {Roo.form.Field} this
37116              * @param {Roo.EventObject} e The event object
37117              */
37118             specialkey : true,
37119             /**
37120              * @event change
37121              * Fires just before the field blurs if the field value has changed.
37122              * @param {Roo.form.Field} this
37123              * @param {Mixed} newValue The new value
37124              * @param {Mixed} oldValue The original value
37125              */
37126             change : true,
37127             /**
37128              * @event invalid
37129              * Fires after the field has been marked as invalid.
37130              * @param {Roo.form.Field} this
37131              * @param {String} msg The validation message
37132              */
37133             invalid : true,
37134             /**
37135              * @event valid
37136              * Fires after the field has been validated with no errors.
37137              * @param {Roo.form.Field} this
37138              */
37139             valid : true,
37140              /**
37141              * @event keyup
37142              * Fires after the key up
37143              * @param {Roo.form.Field} this
37144              * @param {Roo.EventObject}  e The event Object
37145              */
37146             keyup : true
37147         });
37148     },
37149
37150     /**
37151      * Returns the name attribute of the field if available
37152      * @return {String} name The field name
37153      */
37154     getName: function(){
37155          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37156     },
37157
37158     // private
37159     onRender : function(ct, position){
37160         Roo.form.Field.superclass.onRender.call(this, ct, position);
37161         if(!this.el){
37162             var cfg = this.getAutoCreate();
37163             if(!cfg.name){
37164                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37165             }
37166             if (!cfg.name.length) {
37167                 delete cfg.name;
37168             }
37169             if(this.inputType){
37170                 cfg.type = this.inputType;
37171             }
37172             this.el = ct.createChild(cfg, position);
37173         }
37174         var type = this.el.dom.type;
37175         if(type){
37176             if(type == 'password'){
37177                 type = 'text';
37178             }
37179             this.el.addClass('x-form-'+type);
37180         }
37181         if(this.readOnly){
37182             this.el.dom.readOnly = true;
37183         }
37184         if(this.tabIndex !== undefined){
37185             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37186         }
37187
37188         this.el.addClass([this.fieldClass, this.cls]);
37189         this.initValue();
37190     },
37191
37192     /**
37193      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37194      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37195      * @return {Roo.form.Field} this
37196      */
37197     applyTo : function(target){
37198         this.allowDomMove = false;
37199         this.el = Roo.get(target);
37200         this.render(this.el.dom.parentNode);
37201         return this;
37202     },
37203
37204     // private
37205     initValue : function(){
37206         if(this.value !== undefined){
37207             this.setValue(this.value);
37208         }else if(this.el.dom.value.length > 0){
37209             this.setValue(this.el.dom.value);
37210         }
37211     },
37212
37213     /**
37214      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37215      */
37216     isDirty : function() {
37217         if(this.disabled) {
37218             return false;
37219         }
37220         return String(this.getValue()) !== String(this.originalValue);
37221     },
37222
37223     // private
37224     afterRender : function(){
37225         Roo.form.Field.superclass.afterRender.call(this);
37226         this.initEvents();
37227     },
37228
37229     // private
37230     fireKey : function(e){
37231         //Roo.log('field ' + e.getKey());
37232         if(e.isNavKeyPress()){
37233             this.fireEvent("specialkey", this, e);
37234         }
37235     },
37236
37237     /**
37238      * Resets the current field value to the originally loaded value and clears any validation messages
37239      */
37240     reset : function(){
37241         this.setValue(this.resetValue);
37242         this.clearInvalid();
37243     },
37244
37245     // private
37246     initEvents : function(){
37247         // safari killled keypress - so keydown is now used..
37248         this.el.on("keydown" , this.fireKey,  this);
37249         this.el.on("focus", this.onFocus,  this);
37250         this.el.on("blur", this.onBlur,  this);
37251         this.el.relayEvent('keyup', this);
37252
37253         // reference to original value for reset
37254         this.originalValue = this.getValue();
37255         this.resetValue =  this.getValue();
37256     },
37257
37258     // private
37259     onFocus : function(){
37260         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37261             this.el.addClass(this.focusClass);
37262         }
37263         if(!this.hasFocus){
37264             this.hasFocus = true;
37265             this.startValue = this.getValue();
37266             this.fireEvent("focus", this);
37267         }
37268     },
37269
37270     beforeBlur : Roo.emptyFn,
37271
37272     // private
37273     onBlur : function(){
37274         this.beforeBlur();
37275         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37276             this.el.removeClass(this.focusClass);
37277         }
37278         this.hasFocus = false;
37279         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37280             this.validate();
37281         }
37282         var v = this.getValue();
37283         if(String(v) !== String(this.startValue)){
37284             this.fireEvent('change', this, v, this.startValue);
37285         }
37286         this.fireEvent("blur", this);
37287     },
37288
37289     /**
37290      * Returns whether or not the field value is currently valid
37291      * @param {Boolean} preventMark True to disable marking the field invalid
37292      * @return {Boolean} True if the value is valid, else false
37293      */
37294     isValid : function(preventMark){
37295         if(this.disabled){
37296             return true;
37297         }
37298         var restore = this.preventMark;
37299         this.preventMark = preventMark === true;
37300         var v = this.validateValue(this.processValue(this.getRawValue()));
37301         this.preventMark = restore;
37302         return v;
37303     },
37304
37305     /**
37306      * Validates the field value
37307      * @return {Boolean} True if the value is valid, else false
37308      */
37309     validate : function(){
37310         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37311             this.clearInvalid();
37312             return true;
37313         }
37314         return false;
37315     },
37316
37317     processValue : function(value){
37318         return value;
37319     },
37320
37321     // private
37322     // Subclasses should provide the validation implementation by overriding this
37323     validateValue : function(value){
37324         return true;
37325     },
37326
37327     /**
37328      * Mark this field as invalid
37329      * @param {String} msg The validation message
37330      */
37331     markInvalid : function(msg){
37332         if(!this.rendered || this.preventMark){ // not rendered
37333             return;
37334         }
37335         
37336         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37337         
37338         obj.el.addClass(this.invalidClass);
37339         msg = msg || this.invalidText;
37340         switch(this.msgTarget){
37341             case 'qtip':
37342                 obj.el.dom.qtip = msg;
37343                 obj.el.dom.qclass = 'x-form-invalid-tip';
37344                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37345                     Roo.QuickTips.enable();
37346                 }
37347                 break;
37348             case 'title':
37349                 this.el.dom.title = msg;
37350                 break;
37351             case 'under':
37352                 if(!this.errorEl){
37353                     var elp = this.el.findParent('.x-form-element', 5, true);
37354                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37355                     this.errorEl.setWidth(elp.getWidth(true)-20);
37356                 }
37357                 this.errorEl.update(msg);
37358                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37359                 break;
37360             case 'side':
37361                 if(!this.errorIcon){
37362                     var elp = this.el.findParent('.x-form-element', 5, true);
37363                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37364                 }
37365                 this.alignErrorIcon();
37366                 this.errorIcon.dom.qtip = msg;
37367                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37368                 this.errorIcon.show();
37369                 this.on('resize', this.alignErrorIcon, this);
37370                 break;
37371             default:
37372                 var t = Roo.getDom(this.msgTarget);
37373                 t.innerHTML = msg;
37374                 t.style.display = this.msgDisplay;
37375                 break;
37376         }
37377         this.fireEvent('invalid', this, msg);
37378     },
37379
37380     // private
37381     alignErrorIcon : function(){
37382         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37383     },
37384
37385     /**
37386      * Clear any invalid styles/messages for this field
37387      */
37388     clearInvalid : function(){
37389         if(!this.rendered || this.preventMark){ // not rendered
37390             return;
37391         }
37392         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37393         
37394         obj.el.removeClass(this.invalidClass);
37395         switch(this.msgTarget){
37396             case 'qtip':
37397                 obj.el.dom.qtip = '';
37398                 break;
37399             case 'title':
37400                 this.el.dom.title = '';
37401                 break;
37402             case 'under':
37403                 if(this.errorEl){
37404                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37405                 }
37406                 break;
37407             case 'side':
37408                 if(this.errorIcon){
37409                     this.errorIcon.dom.qtip = '';
37410                     this.errorIcon.hide();
37411                     this.un('resize', this.alignErrorIcon, this);
37412                 }
37413                 break;
37414             default:
37415                 var t = Roo.getDom(this.msgTarget);
37416                 t.innerHTML = '';
37417                 t.style.display = 'none';
37418                 break;
37419         }
37420         this.fireEvent('valid', this);
37421     },
37422
37423     /**
37424      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37425      * @return {Mixed} value The field value
37426      */
37427     getRawValue : function(){
37428         var v = this.el.getValue();
37429         
37430         return v;
37431     },
37432
37433     /**
37434      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37435      * @return {Mixed} value The field value
37436      */
37437     getValue : function(){
37438         var v = this.el.getValue();
37439          
37440         return v;
37441     },
37442
37443     /**
37444      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37445      * @param {Mixed} value The value to set
37446      */
37447     setRawValue : function(v){
37448         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37449     },
37450
37451     /**
37452      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37453      * @param {Mixed} value The value to set
37454      */
37455     setValue : function(v){
37456         this.value = v;
37457         if(this.rendered){
37458             this.el.dom.value = (v === null || v === undefined ? '' : v);
37459              this.validate();
37460         }
37461     },
37462
37463     adjustSize : function(w, h){
37464         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37465         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37466         return s;
37467     },
37468
37469     adjustWidth : function(tag, w){
37470         tag = tag.toLowerCase();
37471         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37472             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37473                 if(tag == 'input'){
37474                     return w + 2;
37475                 }
37476                 if(tag == 'textarea'){
37477                     return w-2;
37478                 }
37479             }else if(Roo.isOpera){
37480                 if(tag == 'input'){
37481                     return w + 2;
37482                 }
37483                 if(tag == 'textarea'){
37484                     return w-2;
37485                 }
37486             }
37487         }
37488         return w;
37489     }
37490 });
37491
37492
37493 // anything other than normal should be considered experimental
37494 Roo.form.Field.msgFx = {
37495     normal : {
37496         show: function(msgEl, f){
37497             msgEl.setDisplayed('block');
37498         },
37499
37500         hide : function(msgEl, f){
37501             msgEl.setDisplayed(false).update('');
37502         }
37503     },
37504
37505     slide : {
37506         show: function(msgEl, f){
37507             msgEl.slideIn('t', {stopFx:true});
37508         },
37509
37510         hide : function(msgEl, f){
37511             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37512         }
37513     },
37514
37515     slideRight : {
37516         show: function(msgEl, f){
37517             msgEl.fixDisplay();
37518             msgEl.alignTo(f.el, 'tl-tr');
37519             msgEl.slideIn('l', {stopFx:true});
37520         },
37521
37522         hide : function(msgEl, f){
37523             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37524         }
37525     }
37526 };/*
37527  * Based on:
37528  * Ext JS Library 1.1.1
37529  * Copyright(c) 2006-2007, Ext JS, LLC.
37530  *
37531  * Originally Released Under LGPL - original licence link has changed is not relivant.
37532  *
37533  * Fork - LGPL
37534  * <script type="text/javascript">
37535  */
37536  
37537
37538 /**
37539  * @class Roo.form.TextField
37540  * @extends Roo.form.Field
37541  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37542  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37543  * @constructor
37544  * Creates a new TextField
37545  * @param {Object} config Configuration options
37546  */
37547 Roo.form.TextField = function(config){
37548     Roo.form.TextField.superclass.constructor.call(this, config);
37549     this.addEvents({
37550         /**
37551          * @event autosize
37552          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37553          * according to the default logic, but this event provides a hook for the developer to apply additional
37554          * logic at runtime to resize the field if needed.
37555              * @param {Roo.form.Field} this This text field
37556              * @param {Number} width The new field width
37557              */
37558         autosize : true
37559     });
37560 };
37561
37562 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37563     /**
37564      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37565      */
37566     grow : false,
37567     /**
37568      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37569      */
37570     growMin : 30,
37571     /**
37572      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37573      */
37574     growMax : 800,
37575     /**
37576      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37577      */
37578     vtype : null,
37579     /**
37580      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37581      */
37582     maskRe : null,
37583     /**
37584      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37585      */
37586     disableKeyFilter : false,
37587     /**
37588      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37589      */
37590     allowBlank : true,
37591     /**
37592      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37593      */
37594     minLength : 0,
37595     /**
37596      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37597      */
37598     maxLength : Number.MAX_VALUE,
37599     /**
37600      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37601      */
37602     minLengthText : "The minimum length for this field is {0}",
37603     /**
37604      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37605      */
37606     maxLengthText : "The maximum length for this field is {0}",
37607     /**
37608      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37609      */
37610     selectOnFocus : false,
37611     /**
37612      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37613      */
37614     blankText : "This field is required",
37615     /**
37616      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37617      * If available, this function will be called only after the basic validators all return true, and will be passed the
37618      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37619      */
37620     validator : null,
37621     /**
37622      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37623      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37624      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37625      */
37626     regex : null,
37627     /**
37628      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37629      */
37630     regexText : "",
37631     /**
37632      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37633      */
37634     emptyText : null,
37635    
37636
37637     // private
37638     initEvents : function()
37639     {
37640         if (this.emptyText) {
37641             this.el.attr('placeholder', this.emptyText);
37642         }
37643         
37644         Roo.form.TextField.superclass.initEvents.call(this);
37645         if(this.validationEvent == 'keyup'){
37646             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37647             this.el.on('keyup', this.filterValidation, this);
37648         }
37649         else if(this.validationEvent !== false){
37650             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37651         }
37652         
37653         if(this.selectOnFocus){
37654             this.on("focus", this.preFocus, this);
37655             
37656         }
37657         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37658             this.el.on("keypress", this.filterKeys, this);
37659         }
37660         if(this.grow){
37661             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37662             this.el.on("click", this.autoSize,  this);
37663         }
37664         if(this.el.is('input[type=password]') && Roo.isSafari){
37665             this.el.on('keydown', this.SafariOnKeyDown, this);
37666         }
37667     },
37668
37669     processValue : function(value){
37670         if(this.stripCharsRe){
37671             var newValue = value.replace(this.stripCharsRe, '');
37672             if(newValue !== value){
37673                 this.setRawValue(newValue);
37674                 return newValue;
37675             }
37676         }
37677         return value;
37678     },
37679
37680     filterValidation : function(e){
37681         if(!e.isNavKeyPress()){
37682             this.validationTask.delay(this.validationDelay);
37683         }
37684     },
37685
37686     // private
37687     onKeyUp : function(e){
37688         if(!e.isNavKeyPress()){
37689             this.autoSize();
37690         }
37691     },
37692
37693     /**
37694      * Resets the current field value to the originally-loaded value and clears any validation messages.
37695      *  
37696      */
37697     reset : function(){
37698         Roo.form.TextField.superclass.reset.call(this);
37699        
37700     },
37701
37702     
37703     // private
37704     preFocus : function(){
37705         
37706         if(this.selectOnFocus){
37707             this.el.dom.select();
37708         }
37709     },
37710
37711     
37712     // private
37713     filterKeys : function(e){
37714         var k = e.getKey();
37715         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37716             return;
37717         }
37718         var c = e.getCharCode(), cc = String.fromCharCode(c);
37719         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37720             return;
37721         }
37722         if(!this.maskRe.test(cc)){
37723             e.stopEvent();
37724         }
37725     },
37726
37727     setValue : function(v){
37728         
37729         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37730         
37731         this.autoSize();
37732     },
37733
37734     /**
37735      * Validates a value according to the field's validation rules and marks the field as invalid
37736      * if the validation fails
37737      * @param {Mixed} value The value to validate
37738      * @return {Boolean} True if the value is valid, else false
37739      */
37740     validateValue : function(value){
37741         if(value.length < 1)  { // if it's blank
37742              if(this.allowBlank){
37743                 this.clearInvalid();
37744                 return true;
37745              }else{
37746                 this.markInvalid(this.blankText);
37747                 return false;
37748              }
37749         }
37750         if(value.length < this.minLength){
37751             this.markInvalid(String.format(this.minLengthText, this.minLength));
37752             return false;
37753         }
37754         if(value.length > this.maxLength){
37755             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37756             return false;
37757         }
37758         if(this.vtype){
37759             var vt = Roo.form.VTypes;
37760             if(!vt[this.vtype](value, this)){
37761                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37762                 return false;
37763             }
37764         }
37765         if(typeof this.validator == "function"){
37766             var msg = this.validator(value);
37767             if(msg !== true){
37768                 this.markInvalid(msg);
37769                 return false;
37770             }
37771         }
37772         if(this.regex && !this.regex.test(value)){
37773             this.markInvalid(this.regexText);
37774             return false;
37775         }
37776         return true;
37777     },
37778
37779     /**
37780      * Selects text in this field
37781      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37782      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37783      */
37784     selectText : function(start, end){
37785         var v = this.getRawValue();
37786         if(v.length > 0){
37787             start = start === undefined ? 0 : start;
37788             end = end === undefined ? v.length : end;
37789             var d = this.el.dom;
37790             if(d.setSelectionRange){
37791                 d.setSelectionRange(start, end);
37792             }else if(d.createTextRange){
37793                 var range = d.createTextRange();
37794                 range.moveStart("character", start);
37795                 range.moveEnd("character", v.length-end);
37796                 range.select();
37797             }
37798         }
37799     },
37800
37801     /**
37802      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37803      * This only takes effect if grow = true, and fires the autosize event.
37804      */
37805     autoSize : function(){
37806         if(!this.grow || !this.rendered){
37807             return;
37808         }
37809         if(!this.metrics){
37810             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37811         }
37812         var el = this.el;
37813         var v = el.dom.value;
37814         var d = document.createElement('div');
37815         d.appendChild(document.createTextNode(v));
37816         v = d.innerHTML;
37817         d = null;
37818         v += "&#160;";
37819         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37820         this.el.setWidth(w);
37821         this.fireEvent("autosize", this, w);
37822     },
37823     
37824     // private
37825     SafariOnKeyDown : function(event)
37826     {
37827         // this is a workaround for a password hang bug on chrome/ webkit.
37828         
37829         var isSelectAll = false;
37830         
37831         if(this.el.dom.selectionEnd > 0){
37832             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37833         }
37834         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37835             event.preventDefault();
37836             this.setValue('');
37837             return;
37838         }
37839         
37840         if(isSelectAll){ // backspace and delete key
37841             
37842             event.preventDefault();
37843             // this is very hacky as keydown always get's upper case.
37844             //
37845             var cc = String.fromCharCode(event.getCharCode());
37846             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37847             
37848         }
37849         
37850         
37851     }
37852 });/*
37853  * Based on:
37854  * Ext JS Library 1.1.1
37855  * Copyright(c) 2006-2007, Ext JS, LLC.
37856  *
37857  * Originally Released Under LGPL - original licence link has changed is not relivant.
37858  *
37859  * Fork - LGPL
37860  * <script type="text/javascript">
37861  */
37862  
37863 /**
37864  * @class Roo.form.Hidden
37865  * @extends Roo.form.TextField
37866  * Simple Hidden element used on forms 
37867  * 
37868  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37869  * 
37870  * @constructor
37871  * Creates a new Hidden form element.
37872  * @param {Object} config Configuration options
37873  */
37874
37875
37876
37877 // easy hidden field...
37878 Roo.form.Hidden = function(config){
37879     Roo.form.Hidden.superclass.constructor.call(this, config);
37880 };
37881   
37882 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37883     fieldLabel:      '',
37884     inputType:      'hidden',
37885     width:          50,
37886     allowBlank:     true,
37887     labelSeparator: '',
37888     hidden:         true,
37889     itemCls :       'x-form-item-display-none'
37890
37891
37892 });
37893
37894
37895 /*
37896  * Based on:
37897  * Ext JS Library 1.1.1
37898  * Copyright(c) 2006-2007, Ext JS, LLC.
37899  *
37900  * Originally Released Under LGPL - original licence link has changed is not relivant.
37901  *
37902  * Fork - LGPL
37903  * <script type="text/javascript">
37904  */
37905  
37906 /**
37907  * @class Roo.form.TriggerField
37908  * @extends Roo.form.TextField
37909  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37910  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37911  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37912  * for which you can provide a custom implementation.  For example:
37913  * <pre><code>
37914 var trigger = new Roo.form.TriggerField();
37915 trigger.onTriggerClick = myTriggerFn;
37916 trigger.applyTo('my-field');
37917 </code></pre>
37918  *
37919  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37920  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37921  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37922  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37923  * @constructor
37924  * Create a new TriggerField.
37925  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37926  * to the base TextField)
37927  */
37928 Roo.form.TriggerField = function(config){
37929     this.mimicing = false;
37930     Roo.form.TriggerField.superclass.constructor.call(this, config);
37931 };
37932
37933 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37934     /**
37935      * @cfg {String} triggerClass A CSS class to apply to the trigger
37936      */
37937     /**
37938      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37939      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37940      */
37941     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37942     /**
37943      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37944      */
37945     hideTrigger:false,
37946
37947     /** @cfg {Boolean} grow @hide */
37948     /** @cfg {Number} growMin @hide */
37949     /** @cfg {Number} growMax @hide */
37950
37951     /**
37952      * @hide 
37953      * @method
37954      */
37955     autoSize: Roo.emptyFn,
37956     // private
37957     monitorTab : true,
37958     // private
37959     deferHeight : true,
37960
37961     
37962     actionMode : 'wrap',
37963     // private
37964     onResize : function(w, h){
37965         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37966         if(typeof w == 'number'){
37967             var x = w - this.trigger.getWidth();
37968             this.el.setWidth(this.adjustWidth('input', x));
37969             this.trigger.setStyle('left', x+'px');
37970         }
37971     },
37972
37973     // private
37974     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37975
37976     // private
37977     getResizeEl : function(){
37978         return this.wrap;
37979     },
37980
37981     // private
37982     getPositionEl : function(){
37983         return this.wrap;
37984     },
37985
37986     // private
37987     alignErrorIcon : function(){
37988         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37989     },
37990
37991     // private
37992     onRender : function(ct, position){
37993         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37994         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37995         this.trigger = this.wrap.createChild(this.triggerConfig ||
37996                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37997         if(this.hideTrigger){
37998             this.trigger.setDisplayed(false);
37999         }
38000         this.initTrigger();
38001         if(!this.width){
38002             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38003         }
38004     },
38005
38006     // private
38007     initTrigger : function(){
38008         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38009         this.trigger.addClassOnOver('x-form-trigger-over');
38010         this.trigger.addClassOnClick('x-form-trigger-click');
38011     },
38012
38013     // private
38014     onDestroy : function(){
38015         if(this.trigger){
38016             this.trigger.removeAllListeners();
38017             this.trigger.remove();
38018         }
38019         if(this.wrap){
38020             this.wrap.remove();
38021         }
38022         Roo.form.TriggerField.superclass.onDestroy.call(this);
38023     },
38024
38025     // private
38026     onFocus : function(){
38027         Roo.form.TriggerField.superclass.onFocus.call(this);
38028         if(!this.mimicing){
38029             this.wrap.addClass('x-trigger-wrap-focus');
38030             this.mimicing = true;
38031             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38032             if(this.monitorTab){
38033                 this.el.on("keydown", this.checkTab, this);
38034             }
38035         }
38036     },
38037
38038     // private
38039     checkTab : function(e){
38040         if(e.getKey() == e.TAB){
38041             this.triggerBlur();
38042         }
38043     },
38044
38045     // private
38046     onBlur : function(){
38047         // do nothing
38048     },
38049
38050     // private
38051     mimicBlur : function(e, t){
38052         if(!this.wrap.contains(t) && this.validateBlur()){
38053             this.triggerBlur();
38054         }
38055     },
38056
38057     // private
38058     triggerBlur : function(){
38059         this.mimicing = false;
38060         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38061         if(this.monitorTab){
38062             this.el.un("keydown", this.checkTab, this);
38063         }
38064         this.wrap.removeClass('x-trigger-wrap-focus');
38065         Roo.form.TriggerField.superclass.onBlur.call(this);
38066     },
38067
38068     // private
38069     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38070     validateBlur : function(e, t){
38071         return true;
38072     },
38073
38074     // private
38075     onDisable : function(){
38076         Roo.form.TriggerField.superclass.onDisable.call(this);
38077         if(this.wrap){
38078             this.wrap.addClass('x-item-disabled');
38079         }
38080     },
38081
38082     // private
38083     onEnable : function(){
38084         Roo.form.TriggerField.superclass.onEnable.call(this);
38085         if(this.wrap){
38086             this.wrap.removeClass('x-item-disabled');
38087         }
38088     },
38089
38090     // private
38091     onShow : function(){
38092         var ae = this.getActionEl();
38093         
38094         if(ae){
38095             ae.dom.style.display = '';
38096             ae.dom.style.visibility = 'visible';
38097         }
38098     },
38099
38100     // private
38101     
38102     onHide : function(){
38103         var ae = this.getActionEl();
38104         ae.dom.style.display = 'none';
38105     },
38106
38107     /**
38108      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38109      * by an implementing function.
38110      * @method
38111      * @param {EventObject} e
38112      */
38113     onTriggerClick : Roo.emptyFn
38114 });
38115
38116 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38117 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38118 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38119 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38120     initComponent : function(){
38121         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38122
38123         this.triggerConfig = {
38124             tag:'span', cls:'x-form-twin-triggers', cn:[
38125             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38126             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38127         ]};
38128     },
38129
38130     getTrigger : function(index){
38131         return this.triggers[index];
38132     },
38133
38134     initTrigger : function(){
38135         var ts = this.trigger.select('.x-form-trigger', true);
38136         this.wrap.setStyle('overflow', 'hidden');
38137         var triggerField = this;
38138         ts.each(function(t, all, index){
38139             t.hide = function(){
38140                 var w = triggerField.wrap.getWidth();
38141                 this.dom.style.display = 'none';
38142                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38143             };
38144             t.show = function(){
38145                 var w = triggerField.wrap.getWidth();
38146                 this.dom.style.display = '';
38147                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38148             };
38149             var triggerIndex = 'Trigger'+(index+1);
38150
38151             if(this['hide'+triggerIndex]){
38152                 t.dom.style.display = 'none';
38153             }
38154             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38155             t.addClassOnOver('x-form-trigger-over');
38156             t.addClassOnClick('x-form-trigger-click');
38157         }, this);
38158         this.triggers = ts.elements;
38159     },
38160
38161     onTrigger1Click : Roo.emptyFn,
38162     onTrigger2Click : Roo.emptyFn
38163 });/*
38164  * Based on:
38165  * Ext JS Library 1.1.1
38166  * Copyright(c) 2006-2007, Ext JS, LLC.
38167  *
38168  * Originally Released Under LGPL - original licence link has changed is not relivant.
38169  *
38170  * Fork - LGPL
38171  * <script type="text/javascript">
38172  */
38173  
38174 /**
38175  * @class Roo.form.TextArea
38176  * @extends Roo.form.TextField
38177  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38178  * support for auto-sizing.
38179  * @constructor
38180  * Creates a new TextArea
38181  * @param {Object} config Configuration options
38182  */
38183 Roo.form.TextArea = function(config){
38184     Roo.form.TextArea.superclass.constructor.call(this, config);
38185     // these are provided exchanges for backwards compat
38186     // minHeight/maxHeight were replaced by growMin/growMax to be
38187     // compatible with TextField growing config values
38188     if(this.minHeight !== undefined){
38189         this.growMin = this.minHeight;
38190     }
38191     if(this.maxHeight !== undefined){
38192         this.growMax = this.maxHeight;
38193     }
38194 };
38195
38196 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38197     /**
38198      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38199      */
38200     growMin : 60,
38201     /**
38202      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38203      */
38204     growMax: 1000,
38205     /**
38206      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38207      * in the field (equivalent to setting overflow: hidden, defaults to false)
38208      */
38209     preventScrollbars: false,
38210     /**
38211      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38212      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38213      */
38214
38215     // private
38216     onRender : function(ct, position){
38217         if(!this.el){
38218             this.defaultAutoCreate = {
38219                 tag: "textarea",
38220                 style:"width:300px;height:60px;",
38221                 autocomplete: "off"
38222             };
38223         }
38224         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38225         if(this.grow){
38226             this.textSizeEl = Roo.DomHelper.append(document.body, {
38227                 tag: "pre", cls: "x-form-grow-sizer"
38228             });
38229             if(this.preventScrollbars){
38230                 this.el.setStyle("overflow", "hidden");
38231             }
38232             this.el.setHeight(this.growMin);
38233         }
38234     },
38235
38236     onDestroy : function(){
38237         if(this.textSizeEl){
38238             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38239         }
38240         Roo.form.TextArea.superclass.onDestroy.call(this);
38241     },
38242
38243     // private
38244     onKeyUp : function(e){
38245         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38246             this.autoSize();
38247         }
38248     },
38249
38250     /**
38251      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38252      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38253      */
38254     autoSize : function(){
38255         if(!this.grow || !this.textSizeEl){
38256             return;
38257         }
38258         var el = this.el;
38259         var v = el.dom.value;
38260         var ts = this.textSizeEl;
38261
38262         ts.innerHTML = '';
38263         ts.appendChild(document.createTextNode(v));
38264         v = ts.innerHTML;
38265
38266         Roo.fly(ts).setWidth(this.el.getWidth());
38267         if(v.length < 1){
38268             v = "&#160;&#160;";
38269         }else{
38270             if(Roo.isIE){
38271                 v = v.replace(/\n/g, '<p>&#160;</p>');
38272             }
38273             v += "&#160;\n&#160;";
38274         }
38275         ts.innerHTML = v;
38276         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38277         if(h != this.lastHeight){
38278             this.lastHeight = h;
38279             this.el.setHeight(h);
38280             this.fireEvent("autosize", this, h);
38281         }
38282     }
38283 });/*
38284  * Based on:
38285  * Ext JS Library 1.1.1
38286  * Copyright(c) 2006-2007, Ext JS, LLC.
38287  *
38288  * Originally Released Under LGPL - original licence link has changed is not relivant.
38289  *
38290  * Fork - LGPL
38291  * <script type="text/javascript">
38292  */
38293  
38294
38295 /**
38296  * @class Roo.form.NumberField
38297  * @extends Roo.form.TextField
38298  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38299  * @constructor
38300  * Creates a new NumberField
38301  * @param {Object} config Configuration options
38302  */
38303 Roo.form.NumberField = function(config){
38304     Roo.form.NumberField.superclass.constructor.call(this, config);
38305 };
38306
38307 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38308     /**
38309      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38310      */
38311     fieldClass: "x-form-field x-form-num-field",
38312     /**
38313      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38314      */
38315     allowDecimals : true,
38316     /**
38317      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38318      */
38319     decimalSeparator : ".",
38320     /**
38321      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38322      */
38323     decimalPrecision : 2,
38324     /**
38325      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38326      */
38327     allowNegative : true,
38328     /**
38329      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38330      */
38331     minValue : Number.NEGATIVE_INFINITY,
38332     /**
38333      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38334      */
38335     maxValue : Number.MAX_VALUE,
38336     /**
38337      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38338      */
38339     minText : "The minimum value for this field is {0}",
38340     /**
38341      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38342      */
38343     maxText : "The maximum value for this field is {0}",
38344     /**
38345      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38346      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38347      */
38348     nanText : "{0} is not a valid number",
38349
38350     // private
38351     initEvents : function(){
38352         Roo.form.NumberField.superclass.initEvents.call(this);
38353         var allowed = "0123456789";
38354         if(this.allowDecimals){
38355             allowed += this.decimalSeparator;
38356         }
38357         if(this.allowNegative){
38358             allowed += "-";
38359         }
38360         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38361         var keyPress = function(e){
38362             var k = e.getKey();
38363             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38364                 return;
38365             }
38366             var c = e.getCharCode();
38367             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38368                 e.stopEvent();
38369             }
38370         };
38371         this.el.on("keypress", keyPress, this);
38372     },
38373
38374     // private
38375     validateValue : function(value){
38376         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38377             return false;
38378         }
38379         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38380              return true;
38381         }
38382         var num = this.parseValue(value);
38383         if(isNaN(num)){
38384             this.markInvalid(String.format(this.nanText, value));
38385             return false;
38386         }
38387         if(num < this.minValue){
38388             this.markInvalid(String.format(this.minText, this.minValue));
38389             return false;
38390         }
38391         if(num > this.maxValue){
38392             this.markInvalid(String.format(this.maxText, this.maxValue));
38393             return false;
38394         }
38395         return true;
38396     },
38397
38398     getValue : function(){
38399         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38400     },
38401
38402     // private
38403     parseValue : function(value){
38404         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38405         return isNaN(value) ? '' : value;
38406     },
38407
38408     // private
38409     fixPrecision : function(value){
38410         var nan = isNaN(value);
38411         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38412             return nan ? '' : value;
38413         }
38414         return parseFloat(value).toFixed(this.decimalPrecision);
38415     },
38416
38417     setValue : function(v){
38418         v = this.fixPrecision(v);
38419         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38420     },
38421
38422     // private
38423     decimalPrecisionFcn : function(v){
38424         return Math.floor(v);
38425     },
38426
38427     beforeBlur : function(){
38428         var v = this.parseValue(this.getRawValue());
38429         if(v){
38430             this.setValue(v);
38431         }
38432     }
38433 });/*
38434  * Based on:
38435  * Ext JS Library 1.1.1
38436  * Copyright(c) 2006-2007, Ext JS, LLC.
38437  *
38438  * Originally Released Under LGPL - original licence link has changed is not relivant.
38439  *
38440  * Fork - LGPL
38441  * <script type="text/javascript">
38442  */
38443  
38444 /**
38445  * @class Roo.form.DateField
38446  * @extends Roo.form.TriggerField
38447  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38448 * @constructor
38449 * Create a new DateField
38450 * @param {Object} config
38451  */
38452 Roo.form.DateField = function(config){
38453     Roo.form.DateField.superclass.constructor.call(this, config);
38454     
38455       this.addEvents({
38456          
38457         /**
38458          * @event select
38459          * Fires when a date is selected
38460              * @param {Roo.form.DateField} combo This combo box
38461              * @param {Date} date The date selected
38462              */
38463         'select' : true
38464          
38465     });
38466     
38467     
38468     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38469     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38470     this.ddMatch = null;
38471     if(this.disabledDates){
38472         var dd = this.disabledDates;
38473         var re = "(?:";
38474         for(var i = 0; i < dd.length; i++){
38475             re += dd[i];
38476             if(i != dd.length-1) re += "|";
38477         }
38478         this.ddMatch = new RegExp(re + ")");
38479     }
38480 };
38481
38482 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38483     /**
38484      * @cfg {String} format
38485      * The default date format string which can be overriden for localization support.  The format must be
38486      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38487      */
38488     format : "m/d/y",
38489     /**
38490      * @cfg {String} altFormats
38491      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38492      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38493      */
38494     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38495     /**
38496      * @cfg {Array} disabledDays
38497      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38498      */
38499     disabledDays : null,
38500     /**
38501      * @cfg {String} disabledDaysText
38502      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38503      */
38504     disabledDaysText : "Disabled",
38505     /**
38506      * @cfg {Array} disabledDates
38507      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38508      * expression so they are very powerful. Some examples:
38509      * <ul>
38510      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38511      * <li>["03/08", "09/16"] would disable those days for every year</li>
38512      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38513      * <li>["03/../2006"] would disable every day in March 2006</li>
38514      * <li>["^03"] would disable every day in every March</li>
38515      * </ul>
38516      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38517      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38518      */
38519     disabledDates : null,
38520     /**
38521      * @cfg {String} disabledDatesText
38522      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38523      */
38524     disabledDatesText : "Disabled",
38525     /**
38526      * @cfg {Date/String} minValue
38527      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38528      * valid format (defaults to null).
38529      */
38530     minValue : null,
38531     /**
38532      * @cfg {Date/String} maxValue
38533      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38534      * valid format (defaults to null).
38535      */
38536     maxValue : null,
38537     /**
38538      * @cfg {String} minText
38539      * The error text to display when the date in the cell is before minValue (defaults to
38540      * 'The date in this field must be after {minValue}').
38541      */
38542     minText : "The date in this field must be equal to or after {0}",
38543     /**
38544      * @cfg {String} maxText
38545      * The error text to display when the date in the cell is after maxValue (defaults to
38546      * 'The date in this field must be before {maxValue}').
38547      */
38548     maxText : "The date in this field must be equal to or before {0}",
38549     /**
38550      * @cfg {String} invalidText
38551      * The error text to display when the date in the field is invalid (defaults to
38552      * '{value} is not a valid date - it must be in the format {format}').
38553      */
38554     invalidText : "{0} is not a valid date - it must be in the format {1}",
38555     /**
38556      * @cfg {String} triggerClass
38557      * An additional CSS class used to style the trigger button.  The trigger will always get the
38558      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38559      * which displays a calendar icon).
38560      */
38561     triggerClass : 'x-form-date-trigger',
38562     
38563
38564     /**
38565      * @cfg {Boolean} useIso
38566      * if enabled, then the date field will use a hidden field to store the 
38567      * real value as iso formated date. default (false)
38568      */ 
38569     useIso : false,
38570     /**
38571      * @cfg {String/Object} autoCreate
38572      * A DomHelper element spec, or true for a default element spec (defaults to
38573      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38574      */ 
38575     // private
38576     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38577     
38578     // private
38579     hiddenField: false,
38580     
38581     onRender : function(ct, position)
38582     {
38583         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38584         if (this.useIso) {
38585             //this.el.dom.removeAttribute('name'); 
38586             Roo.log("Changing name?");
38587             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38588             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38589                     'before', true);
38590             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38591             // prevent input submission
38592             this.hiddenName = this.name;
38593         }
38594             
38595             
38596     },
38597     
38598     // private
38599     validateValue : function(value)
38600     {
38601         value = this.formatDate(value);
38602         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38603             Roo.log('super failed');
38604             return false;
38605         }
38606         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38607              return true;
38608         }
38609         var svalue = value;
38610         value = this.parseDate(value);
38611         if(!value){
38612             Roo.log('parse date failed' + svalue);
38613             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38614             return false;
38615         }
38616         var time = value.getTime();
38617         if(this.minValue && time < this.minValue.getTime()){
38618             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38619             return false;
38620         }
38621         if(this.maxValue && time > this.maxValue.getTime()){
38622             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38623             return false;
38624         }
38625         if(this.disabledDays){
38626             var day = value.getDay();
38627             for(var i = 0; i < this.disabledDays.length; i++) {
38628                 if(day === this.disabledDays[i]){
38629                     this.markInvalid(this.disabledDaysText);
38630                     return false;
38631                 }
38632             }
38633         }
38634         var fvalue = this.formatDate(value);
38635         if(this.ddMatch && this.ddMatch.test(fvalue)){
38636             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38637             return false;
38638         }
38639         return true;
38640     },
38641
38642     // private
38643     // Provides logic to override the default TriggerField.validateBlur which just returns true
38644     validateBlur : function(){
38645         return !this.menu || !this.menu.isVisible();
38646     },
38647     
38648     getName: function()
38649     {
38650         // returns hidden if it's set..
38651         if (!this.rendered) {return ''};
38652         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38653         
38654     },
38655
38656     /**
38657      * Returns the current date value of the date field.
38658      * @return {Date} The date value
38659      */
38660     getValue : function(){
38661         
38662         return  this.hiddenField ?
38663                 this.hiddenField.value :
38664                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38665     },
38666
38667     /**
38668      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38669      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38670      * (the default format used is "m/d/y").
38671      * <br />Usage:
38672      * <pre><code>
38673 //All of these calls set the same date value (May 4, 2006)
38674
38675 //Pass a date object:
38676 var dt = new Date('5/4/06');
38677 dateField.setValue(dt);
38678
38679 //Pass a date string (default format):
38680 dateField.setValue('5/4/06');
38681
38682 //Pass a date string (custom format):
38683 dateField.format = 'Y-m-d';
38684 dateField.setValue('2006-5-4');
38685 </code></pre>
38686      * @param {String/Date} date The date or valid date string
38687      */
38688     setValue : function(date){
38689         if (this.hiddenField) {
38690             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38691         }
38692         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38693         // make sure the value field is always stored as a date..
38694         this.value = this.parseDate(date);
38695         
38696         
38697     },
38698
38699     // private
38700     parseDate : function(value){
38701         if(!value || value instanceof Date){
38702             return value;
38703         }
38704         var v = Date.parseDate(value, this.format);
38705          if (!v && this.useIso) {
38706             v = Date.parseDate(value, 'Y-m-d');
38707         }
38708         if(!v && this.altFormats){
38709             if(!this.altFormatsArray){
38710                 this.altFormatsArray = this.altFormats.split("|");
38711             }
38712             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38713                 v = Date.parseDate(value, this.altFormatsArray[i]);
38714             }
38715         }
38716         return v;
38717     },
38718
38719     // private
38720     formatDate : function(date, fmt){
38721         return (!date || !(date instanceof Date)) ?
38722                date : date.dateFormat(fmt || this.format);
38723     },
38724
38725     // private
38726     menuListeners : {
38727         select: function(m, d){
38728             
38729             this.setValue(d);
38730             this.fireEvent('select', this, d);
38731         },
38732         show : function(){ // retain focus styling
38733             this.onFocus();
38734         },
38735         hide : function(){
38736             this.focus.defer(10, this);
38737             var ml = this.menuListeners;
38738             this.menu.un("select", ml.select,  this);
38739             this.menu.un("show", ml.show,  this);
38740             this.menu.un("hide", ml.hide,  this);
38741         }
38742     },
38743
38744     // private
38745     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38746     onTriggerClick : function(){
38747         if(this.disabled){
38748             return;
38749         }
38750         if(this.menu == null){
38751             this.menu = new Roo.menu.DateMenu();
38752         }
38753         Roo.apply(this.menu.picker,  {
38754             showClear: this.allowBlank,
38755             minDate : this.minValue,
38756             maxDate : this.maxValue,
38757             disabledDatesRE : this.ddMatch,
38758             disabledDatesText : this.disabledDatesText,
38759             disabledDays : this.disabledDays,
38760             disabledDaysText : this.disabledDaysText,
38761             format : this.useIso ? 'Y-m-d' : this.format,
38762             minText : String.format(this.minText, this.formatDate(this.minValue)),
38763             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38764         });
38765         this.menu.on(Roo.apply({}, this.menuListeners, {
38766             scope:this
38767         }));
38768         this.menu.picker.setValue(this.getValue() || new Date());
38769         this.menu.show(this.el, "tl-bl?");
38770     },
38771
38772     beforeBlur : function(){
38773         var v = this.parseDate(this.getRawValue());
38774         if(v){
38775             this.setValue(v);
38776         }
38777     },
38778
38779     /*@
38780      * overide
38781      * 
38782      */
38783     isDirty : function() {
38784         if(this.disabled) {
38785             return false;
38786         }
38787         
38788         if(typeof(this.startValue) === 'undefined'){
38789             return false;
38790         }
38791         
38792         return String(this.getValue()) !== String(this.startValue);
38793         
38794     }
38795 });/*
38796  * Based on:
38797  * Ext JS Library 1.1.1
38798  * Copyright(c) 2006-2007, Ext JS, LLC.
38799  *
38800  * Originally Released Under LGPL - original licence link has changed is not relivant.
38801  *
38802  * Fork - LGPL
38803  * <script type="text/javascript">
38804  */
38805  
38806 /**
38807  * @class Roo.form.MonthField
38808  * @extends Roo.form.TriggerField
38809  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38810 * @constructor
38811 * Create a new MonthField
38812 * @param {Object} config
38813  */
38814 Roo.form.MonthField = function(config){
38815     
38816     Roo.form.MonthField.superclass.constructor.call(this, config);
38817     
38818       this.addEvents({
38819          
38820         /**
38821          * @event select
38822          * Fires when a date is selected
38823              * @param {Roo.form.MonthFieeld} combo This combo box
38824              * @param {Date} date The date selected
38825              */
38826         'select' : true
38827          
38828     });
38829     
38830     
38831     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38832     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38833     this.ddMatch = null;
38834     if(this.disabledDates){
38835         var dd = this.disabledDates;
38836         var re = "(?:";
38837         for(var i = 0; i < dd.length; i++){
38838             re += dd[i];
38839             if(i != dd.length-1) re += "|";
38840         }
38841         this.ddMatch = new RegExp(re + ")");
38842     }
38843 };
38844
38845 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38846     /**
38847      * @cfg {String} format
38848      * The default date format string which can be overriden for localization support.  The format must be
38849      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38850      */
38851     format : "M Y",
38852     /**
38853      * @cfg {String} altFormats
38854      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38855      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38856      */
38857     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38858     /**
38859      * @cfg {Array} disabledDays
38860      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38861      */
38862     disabledDays : [0,1,2,3,4,5,6],
38863     /**
38864      * @cfg {String} disabledDaysText
38865      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38866      */
38867     disabledDaysText : "Disabled",
38868     /**
38869      * @cfg {Array} disabledDates
38870      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38871      * expression so they are very powerful. Some examples:
38872      * <ul>
38873      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38874      * <li>["03/08", "09/16"] would disable those days for every year</li>
38875      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38876      * <li>["03/../2006"] would disable every day in March 2006</li>
38877      * <li>["^03"] would disable every day in every March</li>
38878      * </ul>
38879      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38880      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38881      */
38882     disabledDates : null,
38883     /**
38884      * @cfg {String} disabledDatesText
38885      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38886      */
38887     disabledDatesText : "Disabled",
38888     /**
38889      * @cfg {Date/String} minValue
38890      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38891      * valid format (defaults to null).
38892      */
38893     minValue : null,
38894     /**
38895      * @cfg {Date/String} maxValue
38896      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38897      * valid format (defaults to null).
38898      */
38899     maxValue : null,
38900     /**
38901      * @cfg {String} minText
38902      * The error text to display when the date in the cell is before minValue (defaults to
38903      * 'The date in this field must be after {minValue}').
38904      */
38905     minText : "The date in this field must be equal to or after {0}",
38906     /**
38907      * @cfg {String} maxTextf
38908      * The error text to display when the date in the cell is after maxValue (defaults to
38909      * 'The date in this field must be before {maxValue}').
38910      */
38911     maxText : "The date in this field must be equal to or before {0}",
38912     /**
38913      * @cfg {String} invalidText
38914      * The error text to display when the date in the field is invalid (defaults to
38915      * '{value} is not a valid date - it must be in the format {format}').
38916      */
38917     invalidText : "{0} is not a valid date - it must be in the format {1}",
38918     /**
38919      * @cfg {String} triggerClass
38920      * An additional CSS class used to style the trigger button.  The trigger will always get the
38921      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38922      * which displays a calendar icon).
38923      */
38924     triggerClass : 'x-form-date-trigger',
38925     
38926
38927     /**
38928      * @cfg {Boolean} useIso
38929      * if enabled, then the date field will use a hidden field to store the 
38930      * real value as iso formated date. default (true)
38931      */ 
38932     useIso : true,
38933     /**
38934      * @cfg {String/Object} autoCreate
38935      * A DomHelper element spec, or true for a default element spec (defaults to
38936      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38937      */ 
38938     // private
38939     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38940     
38941     // private
38942     hiddenField: false,
38943     
38944     hideMonthPicker : false,
38945     
38946     onRender : function(ct, position)
38947     {
38948         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38949         if (this.useIso) {
38950             this.el.dom.removeAttribute('name'); 
38951             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38952                     'before', true);
38953             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38954             // prevent input submission
38955             this.hiddenName = this.name;
38956         }
38957             
38958             
38959     },
38960     
38961     // private
38962     validateValue : function(value)
38963     {
38964         value = this.formatDate(value);
38965         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38966             return false;
38967         }
38968         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38969              return true;
38970         }
38971         var svalue = value;
38972         value = this.parseDate(value);
38973         if(!value){
38974             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38975             return false;
38976         }
38977         var time = value.getTime();
38978         if(this.minValue && time < this.minValue.getTime()){
38979             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38980             return false;
38981         }
38982         if(this.maxValue && time > this.maxValue.getTime()){
38983             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38984             return false;
38985         }
38986         /*if(this.disabledDays){
38987             var day = value.getDay();
38988             for(var i = 0; i < this.disabledDays.length; i++) {
38989                 if(day === this.disabledDays[i]){
38990                     this.markInvalid(this.disabledDaysText);
38991                     return false;
38992                 }
38993             }
38994         }
38995         */
38996         var fvalue = this.formatDate(value);
38997         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38998             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38999             return false;
39000         }
39001         */
39002         return true;
39003     },
39004
39005     // private
39006     // Provides logic to override the default TriggerField.validateBlur which just returns true
39007     validateBlur : function(){
39008         return !this.menu || !this.menu.isVisible();
39009     },
39010
39011     /**
39012      * Returns the current date value of the date field.
39013      * @return {Date} The date value
39014      */
39015     getValue : function(){
39016         
39017         
39018         
39019         return  this.hiddenField ?
39020                 this.hiddenField.value :
39021                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39022     },
39023
39024     /**
39025      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39026      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39027      * (the default format used is "m/d/y").
39028      * <br />Usage:
39029      * <pre><code>
39030 //All of these calls set the same date value (May 4, 2006)
39031
39032 //Pass a date object:
39033 var dt = new Date('5/4/06');
39034 monthField.setValue(dt);
39035
39036 //Pass a date string (default format):
39037 monthField.setValue('5/4/06');
39038
39039 //Pass a date string (custom format):
39040 monthField.format = 'Y-m-d';
39041 monthField.setValue('2006-5-4');
39042 </code></pre>
39043      * @param {String/Date} date The date or valid date string
39044      */
39045     setValue : function(date){
39046         Roo.log('month setValue' + date);
39047         // can only be first of month..
39048         
39049         var val = this.parseDate(date);
39050         
39051         if (this.hiddenField) {
39052             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39053         }
39054         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39055         this.value = this.parseDate(date);
39056     },
39057
39058     // private
39059     parseDate : function(value){
39060         if(!value || value instanceof Date){
39061             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39062             return value;
39063         }
39064         var v = Date.parseDate(value, this.format);
39065         if (!v && this.useIso) {
39066             v = Date.parseDate(value, 'Y-m-d');
39067         }
39068         if (v) {
39069             // 
39070             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39071         }
39072         
39073         
39074         if(!v && this.altFormats){
39075             if(!this.altFormatsArray){
39076                 this.altFormatsArray = this.altFormats.split("|");
39077             }
39078             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39079                 v = Date.parseDate(value, this.altFormatsArray[i]);
39080             }
39081         }
39082         return v;
39083     },
39084
39085     // private
39086     formatDate : function(date, fmt){
39087         return (!date || !(date instanceof Date)) ?
39088                date : date.dateFormat(fmt || this.format);
39089     },
39090
39091     // private
39092     menuListeners : {
39093         select: function(m, d){
39094             this.setValue(d);
39095             this.fireEvent('select', this, d);
39096         },
39097         show : function(){ // retain focus styling
39098             this.onFocus();
39099         },
39100         hide : function(){
39101             this.focus.defer(10, this);
39102             var ml = this.menuListeners;
39103             this.menu.un("select", ml.select,  this);
39104             this.menu.un("show", ml.show,  this);
39105             this.menu.un("hide", ml.hide,  this);
39106         }
39107     },
39108     // private
39109     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39110     onTriggerClick : function(){
39111         if(this.disabled){
39112             return;
39113         }
39114         if(this.menu == null){
39115             this.menu = new Roo.menu.DateMenu();
39116            
39117         }
39118         
39119         Roo.apply(this.menu.picker,  {
39120             
39121             showClear: this.allowBlank,
39122             minDate : this.minValue,
39123             maxDate : this.maxValue,
39124             disabledDatesRE : this.ddMatch,
39125             disabledDatesText : this.disabledDatesText,
39126             
39127             format : this.useIso ? 'Y-m-d' : this.format,
39128             minText : String.format(this.minText, this.formatDate(this.minValue)),
39129             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39130             
39131         });
39132          this.menu.on(Roo.apply({}, this.menuListeners, {
39133             scope:this
39134         }));
39135        
39136         
39137         var m = this.menu;
39138         var p = m.picker;
39139         
39140         // hide month picker get's called when we called by 'before hide';
39141         
39142         var ignorehide = true;
39143         p.hideMonthPicker  = function(disableAnim){
39144             if (ignorehide) {
39145                 return;
39146             }
39147              if(this.monthPicker){
39148                 Roo.log("hideMonthPicker called");
39149                 if(disableAnim === true){
39150                     this.monthPicker.hide();
39151                 }else{
39152                     this.monthPicker.slideOut('t', {duration:.2});
39153                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39154                     p.fireEvent("select", this, this.value);
39155                     m.hide();
39156                 }
39157             }
39158         }
39159         
39160         Roo.log('picker set value');
39161         Roo.log(this.getValue());
39162         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39163         m.show(this.el, 'tl-bl?');
39164         ignorehide  = false;
39165         // this will trigger hideMonthPicker..
39166         
39167         
39168         // hidden the day picker
39169         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39170         
39171         
39172         
39173       
39174         
39175         p.showMonthPicker.defer(100, p);
39176     
39177         
39178        
39179     },
39180
39181     beforeBlur : function(){
39182         var v = this.parseDate(this.getRawValue());
39183         if(v){
39184             this.setValue(v);
39185         }
39186     }
39187
39188     /** @cfg {Boolean} grow @hide */
39189     /** @cfg {Number} growMin @hide */
39190     /** @cfg {Number} growMax @hide */
39191     /**
39192      * @hide
39193      * @method autoSize
39194      */
39195 });/*
39196  * Based on:
39197  * Ext JS Library 1.1.1
39198  * Copyright(c) 2006-2007, Ext JS, LLC.
39199  *
39200  * Originally Released Under LGPL - original licence link has changed is not relivant.
39201  *
39202  * Fork - LGPL
39203  * <script type="text/javascript">
39204  */
39205  
39206
39207 /**
39208  * @class Roo.form.ComboBox
39209  * @extends Roo.form.TriggerField
39210  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39211  * @constructor
39212  * Create a new ComboBox.
39213  * @param {Object} config Configuration options
39214  */
39215 Roo.form.ComboBox = function(config){
39216     Roo.form.ComboBox.superclass.constructor.call(this, config);
39217     this.addEvents({
39218         /**
39219          * @event expand
39220          * Fires when the dropdown list is expanded
39221              * @param {Roo.form.ComboBox} combo This combo box
39222              */
39223         'expand' : true,
39224         /**
39225          * @event collapse
39226          * Fires when the dropdown list is collapsed
39227              * @param {Roo.form.ComboBox} combo This combo box
39228              */
39229         'collapse' : true,
39230         /**
39231          * @event beforeselect
39232          * Fires before a list item is selected. Return false to cancel the selection.
39233              * @param {Roo.form.ComboBox} combo This combo box
39234              * @param {Roo.data.Record} record The data record returned from the underlying store
39235              * @param {Number} index The index of the selected item in the dropdown list
39236              */
39237         'beforeselect' : true,
39238         /**
39239          * @event select
39240          * Fires when a list item is selected
39241              * @param {Roo.form.ComboBox} combo This combo box
39242              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39243              * @param {Number} index The index of the selected item in the dropdown list
39244              */
39245         'select' : true,
39246         /**
39247          * @event beforequery
39248          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39249          * The event object passed has these properties:
39250              * @param {Roo.form.ComboBox} combo This combo box
39251              * @param {String} query The query
39252              * @param {Boolean} forceAll true to force "all" query
39253              * @param {Boolean} cancel true to cancel the query
39254              * @param {Object} e The query event object
39255              */
39256         'beforequery': true,
39257          /**
39258          * @event add
39259          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39260              * @param {Roo.form.ComboBox} combo This combo box
39261              */
39262         'add' : true,
39263         /**
39264          * @event edit
39265          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39266              * @param {Roo.form.ComboBox} combo This combo box
39267              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39268              */
39269         'edit' : true
39270         
39271         
39272     });
39273     if(this.transform){
39274         this.allowDomMove = false;
39275         var s = Roo.getDom(this.transform);
39276         if(!this.hiddenName){
39277             this.hiddenName = s.name;
39278         }
39279         if(!this.store){
39280             this.mode = 'local';
39281             var d = [], opts = s.options;
39282             for(var i = 0, len = opts.length;i < len; i++){
39283                 var o = opts[i];
39284                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39285                 if(o.selected) {
39286                     this.value = value;
39287                 }
39288                 d.push([value, o.text]);
39289             }
39290             this.store = new Roo.data.SimpleStore({
39291                 'id': 0,
39292                 fields: ['value', 'text'],
39293                 data : d
39294             });
39295             this.valueField = 'value';
39296             this.displayField = 'text';
39297         }
39298         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39299         if(!this.lazyRender){
39300             this.target = true;
39301             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39302             s.parentNode.removeChild(s); // remove it
39303             this.render(this.el.parentNode);
39304         }else{
39305             s.parentNode.removeChild(s); // remove it
39306         }
39307
39308     }
39309     if (this.store) {
39310         this.store = Roo.factory(this.store, Roo.data);
39311     }
39312     
39313     this.selectedIndex = -1;
39314     if(this.mode == 'local'){
39315         if(config.queryDelay === undefined){
39316             this.queryDelay = 10;
39317         }
39318         if(config.minChars === undefined){
39319             this.minChars = 0;
39320         }
39321     }
39322 };
39323
39324 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39325     /**
39326      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39327      */
39328     /**
39329      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39330      * rendering into an Roo.Editor, defaults to false)
39331      */
39332     /**
39333      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39334      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39335      */
39336     /**
39337      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39338      */
39339     /**
39340      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39341      * the dropdown list (defaults to undefined, with no header element)
39342      */
39343
39344      /**
39345      * @cfg {String/Roo.Template} tpl The template to use to render the output
39346      */
39347      
39348     // private
39349     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39350     /**
39351      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39352      */
39353     listWidth: undefined,
39354     /**
39355      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39356      * mode = 'remote' or 'text' if mode = 'local')
39357      */
39358     displayField: undefined,
39359     /**
39360      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39361      * mode = 'remote' or 'value' if mode = 'local'). 
39362      * Note: use of a valueField requires the user make a selection
39363      * in order for a value to be mapped.
39364      */
39365     valueField: undefined,
39366     
39367     
39368     /**
39369      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39370      * field's data value (defaults to the underlying DOM element's name)
39371      */
39372     hiddenName: undefined,
39373     /**
39374      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39375      */
39376     listClass: '',
39377     /**
39378      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39379      */
39380     selectedClass: 'x-combo-selected',
39381     /**
39382      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39383      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39384      * which displays a downward arrow icon).
39385      */
39386     triggerClass : 'x-form-arrow-trigger',
39387     /**
39388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39389      */
39390     shadow:'sides',
39391     /**
39392      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39393      * anchor positions (defaults to 'tl-bl')
39394      */
39395     listAlign: 'tl-bl?',
39396     /**
39397      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39398      */
39399     maxHeight: 300,
39400     /**
39401      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39402      * query specified by the allQuery config option (defaults to 'query')
39403      */
39404     triggerAction: 'query',
39405     /**
39406      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39407      * (defaults to 4, does not apply if editable = false)
39408      */
39409     minChars : 4,
39410     /**
39411      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39412      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39413      */
39414     typeAhead: false,
39415     /**
39416      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39417      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39418      */
39419     queryDelay: 500,
39420     /**
39421      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39422      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39423      */
39424     pageSize: 0,
39425     /**
39426      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39427      * when editable = true (defaults to false)
39428      */
39429     selectOnFocus:false,
39430     /**
39431      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39432      */
39433     queryParam: 'query',
39434     /**
39435      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39436      * when mode = 'remote' (defaults to 'Loading...')
39437      */
39438     loadingText: 'Loading...',
39439     /**
39440      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39441      */
39442     resizable: false,
39443     /**
39444      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39445      */
39446     handleHeight : 8,
39447     /**
39448      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39449      * traditional select (defaults to true)
39450      */
39451     editable: true,
39452     /**
39453      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39454      */
39455     allQuery: '',
39456     /**
39457      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39458      */
39459     mode: 'remote',
39460     /**
39461      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39462      * listWidth has a higher value)
39463      */
39464     minListWidth : 70,
39465     /**
39466      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39467      * allow the user to set arbitrary text into the field (defaults to false)
39468      */
39469     forceSelection:false,
39470     /**
39471      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39472      * if typeAhead = true (defaults to 250)
39473      */
39474     typeAheadDelay : 250,
39475     /**
39476      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39477      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39478      */
39479     valueNotFoundText : undefined,
39480     /**
39481      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39482      */
39483     blockFocus : false,
39484     
39485     /**
39486      * @cfg {Boolean} disableClear Disable showing of clear button.
39487      */
39488     disableClear : false,
39489     /**
39490      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39491      */
39492     alwaysQuery : false,
39493     
39494     //private
39495     addicon : false,
39496     editicon: false,
39497     
39498     // element that contains real text value.. (when hidden is used..)
39499      
39500     // private
39501     onRender : function(ct, position){
39502         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39503         if(this.hiddenName){
39504             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39505                     'before', true);
39506             this.hiddenField.value =
39507                 this.hiddenValue !== undefined ? this.hiddenValue :
39508                 this.value !== undefined ? this.value : '';
39509
39510             // prevent input submission
39511             this.el.dom.removeAttribute('name');
39512              
39513              
39514         }
39515         if(Roo.isGecko){
39516             this.el.dom.setAttribute('autocomplete', 'off');
39517         }
39518
39519         var cls = 'x-combo-list';
39520
39521         this.list = new Roo.Layer({
39522             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39523         });
39524
39525         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39526         this.list.setWidth(lw);
39527         this.list.swallowEvent('mousewheel');
39528         this.assetHeight = 0;
39529
39530         if(this.title){
39531             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39532             this.assetHeight += this.header.getHeight();
39533         }
39534
39535         this.innerList = this.list.createChild({cls:cls+'-inner'});
39536         this.innerList.on('mouseover', this.onViewOver, this);
39537         this.innerList.on('mousemove', this.onViewMove, this);
39538         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39539         
39540         if(this.allowBlank && !this.pageSize && !this.disableClear){
39541             this.footer = this.list.createChild({cls:cls+'-ft'});
39542             this.pageTb = new Roo.Toolbar(this.footer);
39543            
39544         }
39545         if(this.pageSize){
39546             this.footer = this.list.createChild({cls:cls+'-ft'});
39547             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39548                     {pageSize: this.pageSize});
39549             
39550         }
39551         
39552         if (this.pageTb && this.allowBlank && !this.disableClear) {
39553             var _this = this;
39554             this.pageTb.add(new Roo.Toolbar.Fill(), {
39555                 cls: 'x-btn-icon x-btn-clear',
39556                 text: '&#160;',
39557                 handler: function()
39558                 {
39559                     _this.collapse();
39560                     _this.clearValue();
39561                     _this.onSelect(false, -1);
39562                 }
39563             });
39564         }
39565         if (this.footer) {
39566             this.assetHeight += this.footer.getHeight();
39567         }
39568         
39569
39570         if(!this.tpl){
39571             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39572         }
39573
39574         this.view = new Roo.View(this.innerList, this.tpl, {
39575             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39576         });
39577
39578         this.view.on('click', this.onViewClick, this);
39579
39580         this.store.on('beforeload', this.onBeforeLoad, this);
39581         this.store.on('load', this.onLoad, this);
39582         this.store.on('loadexception', this.onLoadException, this);
39583
39584         if(this.resizable){
39585             this.resizer = new Roo.Resizable(this.list,  {
39586                pinned:true, handles:'se'
39587             });
39588             this.resizer.on('resize', function(r, w, h){
39589                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39590                 this.listWidth = w;
39591                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39592                 this.restrictHeight();
39593             }, this);
39594             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39595         }
39596         if(!this.editable){
39597             this.editable = true;
39598             this.setEditable(false);
39599         }  
39600         
39601         
39602         if (typeof(this.events.add.listeners) != 'undefined') {
39603             
39604             this.addicon = this.wrap.createChild(
39605                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39606        
39607             this.addicon.on('click', function(e) {
39608                 this.fireEvent('add', this);
39609             }, this);
39610         }
39611         if (typeof(this.events.edit.listeners) != 'undefined') {
39612             
39613             this.editicon = this.wrap.createChild(
39614                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39615             if (this.addicon) {
39616                 this.editicon.setStyle('margin-left', '40px');
39617             }
39618             this.editicon.on('click', function(e) {
39619                 
39620                 // we fire even  if inothing is selected..
39621                 this.fireEvent('edit', this, this.lastData );
39622                 
39623             }, this);
39624         }
39625         
39626         
39627         
39628     },
39629
39630     // private
39631     initEvents : function(){
39632         Roo.form.ComboBox.superclass.initEvents.call(this);
39633
39634         this.keyNav = new Roo.KeyNav(this.el, {
39635             "up" : function(e){
39636                 this.inKeyMode = true;
39637                 this.selectPrev();
39638             },
39639
39640             "down" : function(e){
39641                 if(!this.isExpanded()){
39642                     this.onTriggerClick();
39643                 }else{
39644                     this.inKeyMode = true;
39645                     this.selectNext();
39646                 }
39647             },
39648
39649             "enter" : function(e){
39650                 this.onViewClick();
39651                 //return true;
39652             },
39653
39654             "esc" : function(e){
39655                 this.collapse();
39656             },
39657
39658             "tab" : function(e){
39659                 this.onViewClick(false);
39660                 this.fireEvent("specialkey", this, e);
39661                 return true;
39662             },
39663
39664             scope : this,
39665
39666             doRelay : function(foo, bar, hname){
39667                 if(hname == 'down' || this.scope.isExpanded()){
39668                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39669                 }
39670                 return true;
39671             },
39672
39673             forceKeyDown: true
39674         });
39675         this.queryDelay = Math.max(this.queryDelay || 10,
39676                 this.mode == 'local' ? 10 : 250);
39677         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39678         if(this.typeAhead){
39679             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39680         }
39681         if(this.editable !== false){
39682             this.el.on("keyup", this.onKeyUp, this);
39683         }
39684         if(this.forceSelection){
39685             this.on('blur', this.doForce, this);
39686         }
39687     },
39688
39689     onDestroy : function(){
39690         if(this.view){
39691             this.view.setStore(null);
39692             this.view.el.removeAllListeners();
39693             this.view.el.remove();
39694             this.view.purgeListeners();
39695         }
39696         if(this.list){
39697             this.list.destroy();
39698         }
39699         if(this.store){
39700             this.store.un('beforeload', this.onBeforeLoad, this);
39701             this.store.un('load', this.onLoad, this);
39702             this.store.un('loadexception', this.onLoadException, this);
39703         }
39704         Roo.form.ComboBox.superclass.onDestroy.call(this);
39705     },
39706
39707     // private
39708     fireKey : function(e){
39709         if(e.isNavKeyPress() && !this.list.isVisible()){
39710             this.fireEvent("specialkey", this, e);
39711         }
39712     },
39713
39714     // private
39715     onResize: function(w, h){
39716         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39717         
39718         if(typeof w != 'number'){
39719             // we do not handle it!?!?
39720             return;
39721         }
39722         var tw = this.trigger.getWidth();
39723         tw += this.addicon ? this.addicon.getWidth() : 0;
39724         tw += this.editicon ? this.editicon.getWidth() : 0;
39725         var x = w - tw;
39726         this.el.setWidth( this.adjustWidth('input', x));
39727             
39728         this.trigger.setStyle('left', x+'px');
39729         
39730         if(this.list && this.listWidth === undefined){
39731             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39732             this.list.setWidth(lw);
39733             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39734         }
39735         
39736     
39737         
39738     },
39739
39740     /**
39741      * Allow or prevent the user from directly editing the field text.  If false is passed,
39742      * the user will only be able to select from the items defined in the dropdown list.  This method
39743      * is the runtime equivalent of setting the 'editable' config option at config time.
39744      * @param {Boolean} value True to allow the user to directly edit the field text
39745      */
39746     setEditable : function(value){
39747         if(value == this.editable){
39748             return;
39749         }
39750         this.editable = value;
39751         if(!value){
39752             this.el.dom.setAttribute('readOnly', true);
39753             this.el.on('mousedown', this.onTriggerClick,  this);
39754             this.el.addClass('x-combo-noedit');
39755         }else{
39756             this.el.dom.setAttribute('readOnly', false);
39757             this.el.un('mousedown', this.onTriggerClick,  this);
39758             this.el.removeClass('x-combo-noedit');
39759         }
39760     },
39761
39762     // private
39763     onBeforeLoad : function(){
39764         if(!this.hasFocus){
39765             return;
39766         }
39767         this.innerList.update(this.loadingText ?
39768                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39769         this.restrictHeight();
39770         this.selectedIndex = -1;
39771     },
39772
39773     // private
39774     onLoad : function(){
39775         if(!this.hasFocus){
39776             return;
39777         }
39778         if(this.store.getCount() > 0){
39779             this.expand();
39780             this.restrictHeight();
39781             if(this.lastQuery == this.allQuery){
39782                 if(this.editable){
39783                     this.el.dom.select();
39784                 }
39785                 if(!this.selectByValue(this.value, true)){
39786                     this.select(0, true);
39787                 }
39788             }else{
39789                 this.selectNext();
39790                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39791                     this.taTask.delay(this.typeAheadDelay);
39792                 }
39793             }
39794         }else{
39795             this.onEmptyResults();
39796         }
39797         //this.el.focus();
39798     },
39799     // private
39800     onLoadException : function()
39801     {
39802         this.collapse();
39803         Roo.log(this.store.reader.jsonData);
39804         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39805             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39806         }
39807         
39808         
39809     },
39810     // private
39811     onTypeAhead : function(){
39812         if(this.store.getCount() > 0){
39813             var r = this.store.getAt(0);
39814             var newValue = r.data[this.displayField];
39815             var len = newValue.length;
39816             var selStart = this.getRawValue().length;
39817             if(selStart != len){
39818                 this.setRawValue(newValue);
39819                 this.selectText(selStart, newValue.length);
39820             }
39821         }
39822     },
39823
39824     // private
39825     onSelect : function(record, index){
39826         if(this.fireEvent('beforeselect', this, record, index) !== false){
39827             this.setFromData(index > -1 ? record.data : false);
39828             this.collapse();
39829             this.fireEvent('select', this, record, index);
39830         }
39831     },
39832
39833     /**
39834      * Returns the currently selected field value or empty string if no value is set.
39835      * @return {String} value The selected value
39836      */
39837     getValue : function(){
39838         if(this.valueField){
39839             return typeof this.value != 'undefined' ? this.value : '';
39840         }
39841         return Roo.form.ComboBox.superclass.getValue.call(this);
39842     },
39843
39844     /**
39845      * Clears any text/value currently set in the field
39846      */
39847     clearValue : function(){
39848         if(this.hiddenField){
39849             this.hiddenField.value = '';
39850         }
39851         this.value = '';
39852         this.setRawValue('');
39853         this.lastSelectionText = '';
39854         
39855     },
39856
39857     /**
39858      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39859      * will be displayed in the field.  If the value does not match the data value of an existing item,
39860      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39861      * Otherwise the field will be blank (although the value will still be set).
39862      * @param {String} value The value to match
39863      */
39864     setValue : function(v){
39865         var text = v;
39866         if(this.valueField){
39867             var r = this.findRecord(this.valueField, v);
39868             if(r){
39869                 text = r.data[this.displayField];
39870             }else if(this.valueNotFoundText !== undefined){
39871                 text = this.valueNotFoundText;
39872             }
39873         }
39874         this.lastSelectionText = text;
39875         if(this.hiddenField){
39876             this.hiddenField.value = v;
39877         }
39878         Roo.form.ComboBox.superclass.setValue.call(this, text);
39879         this.value = v;
39880     },
39881     /**
39882      * @property {Object} the last set data for the element
39883      */
39884     
39885     lastData : false,
39886     /**
39887      * Sets the value of the field based on a object which is related to the record format for the store.
39888      * @param {Object} value the value to set as. or false on reset?
39889      */
39890     setFromData : function(o){
39891         var dv = ''; // display value
39892         var vv = ''; // value value..
39893         this.lastData = o;
39894         if (this.displayField) {
39895             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39896         } else {
39897             // this is an error condition!!!
39898             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39899         }
39900         
39901         if(this.valueField){
39902             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39903         }
39904         if(this.hiddenField){
39905             this.hiddenField.value = vv;
39906             
39907             this.lastSelectionText = dv;
39908             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39909             this.value = vv;
39910             return;
39911         }
39912         // no hidden field.. - we store the value in 'value', but still display
39913         // display field!!!!
39914         this.lastSelectionText = dv;
39915         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39916         this.value = vv;
39917         
39918         
39919     },
39920     // private
39921     reset : function(){
39922         // overridden so that last data is reset..
39923         this.setValue(this.resetValue);
39924         this.clearInvalid();
39925         this.lastData = false;
39926         if (this.view) {
39927             this.view.clearSelections();
39928         }
39929     },
39930     // private
39931     findRecord : function(prop, value){
39932         var record;
39933         if(this.store.getCount() > 0){
39934             this.store.each(function(r){
39935                 if(r.data[prop] == value){
39936                     record = r;
39937                     return false;
39938                 }
39939                 return true;
39940             });
39941         }
39942         return record;
39943     },
39944     
39945     getName: function()
39946     {
39947         // returns hidden if it's set..
39948         if (!this.rendered) {return ''};
39949         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39950         
39951     },
39952     // private
39953     onViewMove : function(e, t){
39954         this.inKeyMode = false;
39955     },
39956
39957     // private
39958     onViewOver : function(e, t){
39959         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39960             return;
39961         }
39962         var item = this.view.findItemFromChild(t);
39963         if(item){
39964             var index = this.view.indexOf(item);
39965             this.select(index, false);
39966         }
39967     },
39968
39969     // private
39970     onViewClick : function(doFocus)
39971     {
39972         var index = this.view.getSelectedIndexes()[0];
39973         var r = this.store.getAt(index);
39974         if(r){
39975             this.onSelect(r, index);
39976         }
39977         if(doFocus !== false && !this.blockFocus){
39978             this.el.focus();
39979         }
39980     },
39981
39982     // private
39983     restrictHeight : function(){
39984         this.innerList.dom.style.height = '';
39985         var inner = this.innerList.dom;
39986         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39987         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39988         this.list.beginUpdate();
39989         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39990         this.list.alignTo(this.el, this.listAlign);
39991         this.list.endUpdate();
39992     },
39993
39994     // private
39995     onEmptyResults : function(){
39996         this.collapse();
39997     },
39998
39999     /**
40000      * Returns true if the dropdown list is expanded, else false.
40001      */
40002     isExpanded : function(){
40003         return this.list.isVisible();
40004     },
40005
40006     /**
40007      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40008      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40009      * @param {String} value The data value of the item to select
40010      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40011      * selected item if it is not currently in view (defaults to true)
40012      * @return {Boolean} True if the value matched an item in the list, else false
40013      */
40014     selectByValue : function(v, scrollIntoView){
40015         if(v !== undefined && v !== null){
40016             var r = this.findRecord(this.valueField || this.displayField, v);
40017             if(r){
40018                 this.select(this.store.indexOf(r), scrollIntoView);
40019                 return true;
40020             }
40021         }
40022         return false;
40023     },
40024
40025     /**
40026      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40027      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40028      * @param {Number} index The zero-based index of the list item to select
40029      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40030      * selected item if it is not currently in view (defaults to true)
40031      */
40032     select : function(index, scrollIntoView){
40033         this.selectedIndex = index;
40034         this.view.select(index);
40035         if(scrollIntoView !== false){
40036             var el = this.view.getNode(index);
40037             if(el){
40038                 this.innerList.scrollChildIntoView(el, false);
40039             }
40040         }
40041     },
40042
40043     // private
40044     selectNext : function(){
40045         var ct = this.store.getCount();
40046         if(ct > 0){
40047             if(this.selectedIndex == -1){
40048                 this.select(0);
40049             }else if(this.selectedIndex < ct-1){
40050                 this.select(this.selectedIndex+1);
40051             }
40052         }
40053     },
40054
40055     // private
40056     selectPrev : function(){
40057         var ct = this.store.getCount();
40058         if(ct > 0){
40059             if(this.selectedIndex == -1){
40060                 this.select(0);
40061             }else if(this.selectedIndex != 0){
40062                 this.select(this.selectedIndex-1);
40063             }
40064         }
40065     },
40066
40067     // private
40068     onKeyUp : function(e){
40069         if(this.editable !== false && !e.isSpecialKey()){
40070             this.lastKey = e.getKey();
40071             this.dqTask.delay(this.queryDelay);
40072         }
40073     },
40074
40075     // private
40076     validateBlur : function(){
40077         return !this.list || !this.list.isVisible();   
40078     },
40079
40080     // private
40081     initQuery : function(){
40082         this.doQuery(this.getRawValue());
40083     },
40084
40085     // private
40086     doForce : function(){
40087         if(this.el.dom.value.length > 0){
40088             this.el.dom.value =
40089                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40090              
40091         }
40092     },
40093
40094     /**
40095      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40096      * query allowing the query action to be canceled if needed.
40097      * @param {String} query The SQL query to execute
40098      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40099      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40100      * saved in the current store (defaults to false)
40101      */
40102     doQuery : function(q, forceAll){
40103         if(q === undefined || q === null){
40104             q = '';
40105         }
40106         var qe = {
40107             query: q,
40108             forceAll: forceAll,
40109             combo: this,
40110             cancel:false
40111         };
40112         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40113             return false;
40114         }
40115         q = qe.query;
40116         forceAll = qe.forceAll;
40117         if(forceAll === true || (q.length >= this.minChars)){
40118             if(this.lastQuery != q || this.alwaysQuery){
40119                 this.lastQuery = q;
40120                 if(this.mode == 'local'){
40121                     this.selectedIndex = -1;
40122                     if(forceAll){
40123                         this.store.clearFilter();
40124                     }else{
40125                         this.store.filter(this.displayField, q);
40126                     }
40127                     this.onLoad();
40128                 }else{
40129                     this.store.baseParams[this.queryParam] = q;
40130                     this.store.load({
40131                         params: this.getParams(q)
40132                     });
40133                     this.expand();
40134                 }
40135             }else{
40136                 this.selectedIndex = -1;
40137                 this.onLoad();   
40138             }
40139         }
40140     },
40141
40142     // private
40143     getParams : function(q){
40144         var p = {};
40145         //p[this.queryParam] = q;
40146         if(this.pageSize){
40147             p.start = 0;
40148             p.limit = this.pageSize;
40149         }
40150         return p;
40151     },
40152
40153     /**
40154      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40155      */
40156     collapse : function(){
40157         if(!this.isExpanded()){
40158             return;
40159         }
40160         this.list.hide();
40161         Roo.get(document).un('mousedown', this.collapseIf, this);
40162         Roo.get(document).un('mousewheel', this.collapseIf, this);
40163         if (!this.editable) {
40164             Roo.get(document).un('keydown', this.listKeyPress, this);
40165         }
40166         this.fireEvent('collapse', this);
40167     },
40168
40169     // private
40170     collapseIf : function(e){
40171         if(!e.within(this.wrap) && !e.within(this.list)){
40172             this.collapse();
40173         }
40174     },
40175
40176     /**
40177      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40178      */
40179     expand : function(){
40180         if(this.isExpanded() || !this.hasFocus){
40181             return;
40182         }
40183         this.list.alignTo(this.el, this.listAlign);
40184         this.list.show();
40185         Roo.get(document).on('mousedown', this.collapseIf, this);
40186         Roo.get(document).on('mousewheel', this.collapseIf, this);
40187         if (!this.editable) {
40188             Roo.get(document).on('keydown', this.listKeyPress, this);
40189         }
40190         
40191         this.fireEvent('expand', this);
40192     },
40193
40194     // private
40195     // Implements the default empty TriggerField.onTriggerClick function
40196     onTriggerClick : function(){
40197         if(this.disabled){
40198             return;
40199         }
40200         if(this.isExpanded()){
40201             this.collapse();
40202             if (!this.blockFocus) {
40203                 this.el.focus();
40204             }
40205             
40206         }else {
40207             this.hasFocus = true;
40208             if(this.triggerAction == 'all') {
40209                 this.doQuery(this.allQuery, true);
40210             } else {
40211                 this.doQuery(this.getRawValue());
40212             }
40213             if (!this.blockFocus) {
40214                 this.el.focus();
40215             }
40216         }
40217     },
40218     listKeyPress : function(e)
40219     {
40220         //Roo.log('listkeypress');
40221         // scroll to first matching element based on key pres..
40222         if (e.isSpecialKey()) {
40223             return false;
40224         }
40225         var k = String.fromCharCode(e.getKey()).toUpperCase();
40226         //Roo.log(k);
40227         var match  = false;
40228         var csel = this.view.getSelectedNodes();
40229         var cselitem = false;
40230         if (csel.length) {
40231             var ix = this.view.indexOf(csel[0]);
40232             cselitem  = this.store.getAt(ix);
40233             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40234                 cselitem = false;
40235             }
40236             
40237         }
40238         
40239         this.store.each(function(v) { 
40240             if (cselitem) {
40241                 // start at existing selection.
40242                 if (cselitem.id == v.id) {
40243                     cselitem = false;
40244                 }
40245                 return;
40246             }
40247                 
40248             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40249                 match = this.store.indexOf(v);
40250                 return false;
40251             }
40252         }, this);
40253         
40254         if (match === false) {
40255             return true; // no more action?
40256         }
40257         // scroll to?
40258         this.view.select(match);
40259         var sn = Roo.get(this.view.getSelectedNodes()[0])
40260         sn.scrollIntoView(sn.dom.parentNode, false);
40261     }
40262
40263     /** 
40264     * @cfg {Boolean} grow 
40265     * @hide 
40266     */
40267     /** 
40268     * @cfg {Number} growMin 
40269     * @hide 
40270     */
40271     /** 
40272     * @cfg {Number} growMax 
40273     * @hide 
40274     */
40275     /**
40276      * @hide
40277      * @method autoSize
40278      */
40279 });/*
40280  * Copyright(c) 2010-2012, Roo J Solutions Limited
40281  *
40282  * Licence LGPL
40283  *
40284  */
40285
40286 /**
40287  * @class Roo.form.ComboBoxArray
40288  * @extends Roo.form.TextField
40289  * A facebook style adder... for lists of email / people / countries  etc...
40290  * pick multiple items from a combo box, and shows each one.
40291  *
40292  *  Fred [x]  Brian [x]  [Pick another |v]
40293  *
40294  *
40295  *  For this to work: it needs various extra information
40296  *    - normal combo problay has
40297  *      name, hiddenName
40298  *    + displayField, valueField
40299  *
40300  *    For our purpose...
40301  *
40302  *
40303  *   If we change from 'extends' to wrapping...
40304  *   
40305  *  
40306  *
40307  
40308  
40309  * @constructor
40310  * Create a new ComboBoxArray.
40311  * @param {Object} config Configuration options
40312  */
40313  
40314
40315 Roo.form.ComboBoxArray = function(config)
40316 {
40317     this.addEvents({
40318         /**
40319          * @event remove
40320          * Fires when remove the value from the list
40321              * @param {Roo.form.ComboBoxArray} _self This combo box array
40322              * @param {Roo.form.ComboBoxArray.Item} item removed item
40323              */
40324         'remove' : true
40325         
40326         
40327     });
40328     
40329     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40330     
40331     this.items = new Roo.util.MixedCollection(false);
40332     
40333     // construct the child combo...
40334     
40335     
40336     
40337     
40338    
40339     
40340 }
40341
40342  
40343 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40344
40345     /**
40346      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40347      */
40348     
40349     lastData : false,
40350     
40351     // behavies liek a hiddne field
40352     inputType:      'hidden',
40353     /**
40354      * @cfg {Number} width The width of the box that displays the selected element
40355      */ 
40356     width:          300,
40357
40358     
40359     
40360     /**
40361      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40362      */
40363     name : false,
40364     /**
40365      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40366      */
40367     hiddenName : false,
40368     
40369     
40370     // private the array of items that are displayed..
40371     items  : false,
40372     // private - the hidden field el.
40373     hiddenEl : false,
40374     // private - the filed el..
40375     el : false,
40376     
40377     //validateValue : function() { return true; }, // all values are ok!
40378     //onAddClick: function() { },
40379     
40380     onRender : function(ct, position) 
40381     {
40382         
40383         // create the standard hidden element
40384         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40385         
40386         
40387         // give fake names to child combo;
40388         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40389         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40390         
40391         this.combo = Roo.factory(this.combo, Roo.form);
40392         this.combo.onRender(ct, position);
40393         if (typeof(this.combo.width) != 'undefined') {
40394             this.combo.onResize(this.combo.width,0);
40395         }
40396         
40397         this.combo.initEvents();
40398         
40399         // assigned so form know we need to do this..
40400         this.store          = this.combo.store;
40401         this.valueField     = this.combo.valueField;
40402         this.displayField   = this.combo.displayField ;
40403         
40404         
40405         this.combo.wrap.addClass('x-cbarray-grp');
40406         
40407         var cbwrap = this.combo.wrap.createChild(
40408             {tag: 'div', cls: 'x-cbarray-cb'},
40409             this.combo.el.dom
40410         );
40411         
40412              
40413         this.hiddenEl = this.combo.wrap.createChild({
40414             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40415         });
40416         this.el = this.combo.wrap.createChild({
40417             tag: 'input',  type:'hidden' , name: this.name, value : ''
40418         });
40419          //   this.el.dom.removeAttribute("name");
40420         
40421         
40422         this.outerWrap = this.combo.wrap;
40423         this.wrap = cbwrap;
40424         
40425         this.outerWrap.setWidth(this.width);
40426         this.outerWrap.dom.removeChild(this.el.dom);
40427         
40428         this.wrap.dom.appendChild(this.el.dom);
40429         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40430         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40431         
40432         this.combo.trigger.setStyle('position','relative');
40433         this.combo.trigger.setStyle('left', '0px');
40434         this.combo.trigger.setStyle('top', '2px');
40435         
40436         this.combo.el.setStyle('vertical-align', 'text-bottom');
40437         
40438         //this.trigger.setStyle('vertical-align', 'top');
40439         
40440         // this should use the code from combo really... on('add' ....)
40441         if (this.adder) {
40442             
40443         
40444             this.adder = this.outerWrap.createChild(
40445                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40446             var _t = this;
40447             this.adder.on('click', function(e) {
40448                 _t.fireEvent('adderclick', this, e);
40449             }, _t);
40450         }
40451         //var _t = this;
40452         //this.adder.on('click', this.onAddClick, _t);
40453         
40454         
40455         this.combo.on('select', function(cb, rec, ix) {
40456             this.addItem(rec.data);
40457             
40458             cb.setValue('');
40459             cb.el.dom.value = '';
40460             //cb.lastData = rec.data;
40461             // add to list
40462             
40463         }, this);
40464         
40465         
40466     },
40467     
40468     
40469     getName: function()
40470     {
40471         // returns hidden if it's set..
40472         if (!this.rendered) {return ''};
40473         return  this.hiddenName ? this.hiddenName : this.name;
40474         
40475     },
40476     
40477     
40478     onResize: function(w, h){
40479         
40480         return;
40481         // not sure if this is needed..
40482         //this.combo.onResize(w,h);
40483         
40484         if(typeof w != 'number'){
40485             // we do not handle it!?!?
40486             return;
40487         }
40488         var tw = this.combo.trigger.getWidth();
40489         tw += this.addicon ? this.addicon.getWidth() : 0;
40490         tw += this.editicon ? this.editicon.getWidth() : 0;
40491         var x = w - tw;
40492         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40493             
40494         this.combo.trigger.setStyle('left', '0px');
40495         
40496         if(this.list && this.listWidth === undefined){
40497             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40498             this.list.setWidth(lw);
40499             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40500         }
40501         
40502     
40503         
40504     },
40505     
40506     addItem: function(rec)
40507     {
40508         var valueField = this.combo.valueField;
40509         var displayField = this.combo.displayField;
40510         if (this.items.indexOfKey(rec[valueField]) > -1) {
40511             //console.log("GOT " + rec.data.id);
40512             return;
40513         }
40514         
40515         var x = new Roo.form.ComboBoxArray.Item({
40516             //id : rec[this.idField],
40517             data : rec,
40518             displayField : displayField ,
40519             tipField : displayField ,
40520             cb : this
40521         });
40522         // use the 
40523         this.items.add(rec[valueField],x);
40524         // add it before the element..
40525         this.updateHiddenEl();
40526         x.render(this.outerWrap, this.wrap.dom);
40527         // add the image handler..
40528     },
40529     
40530     updateHiddenEl : function()
40531     {
40532         this.validate();
40533         if (!this.hiddenEl) {
40534             return;
40535         }
40536         var ar = [];
40537         var idField = this.combo.valueField;
40538         
40539         this.items.each(function(f) {
40540             ar.push(f.data[idField]);
40541            
40542         });
40543         this.hiddenEl.dom.value = ar.join(',');
40544         this.validate();
40545     },
40546     
40547     reset : function()
40548     {
40549         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40550         this.items.each(function(f) {
40551            f.remove(); 
40552         });
40553         this.el.dom.value = '';
40554         if (this.hiddenEl) {
40555             this.hiddenEl.dom.value = '';
40556         }
40557         
40558     },
40559     getValue: function()
40560     {
40561         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40562     },
40563     setValue: function(v) // not a valid action - must use addItems..
40564     {
40565          
40566         this.reset();
40567         
40568         
40569         
40570         if (this.store.isLocal && (typeof(v) == 'string')) {
40571             // then we can use the store to find the values..
40572             // comma seperated at present.. this needs to allow JSON based encoding..
40573             this.hiddenEl.value  = v;
40574             var v_ar = [];
40575             Roo.each(v.split(','), function(k) {
40576                 Roo.log("CHECK " + this.valueField + ',' + k);
40577                 var li = this.store.query(this.valueField, k);
40578                 if (!li.length) {
40579                     return;
40580                 }
40581                 var add = {};
40582                 add[this.valueField] = k;
40583                 add[this.displayField] = li.item(0).data[this.displayField];
40584                 
40585                 this.addItem(add);
40586             }, this) 
40587              
40588         }
40589         if (typeof(v) == 'object' ) {
40590             // then let's assume it's an array of objects..
40591             Roo.each(v, function(l) {
40592                 this.addItem(l);
40593             }, this);
40594              
40595         }
40596         
40597         
40598     },
40599     setFromData: function(v)
40600     {
40601         // this recieves an object, if setValues is called.
40602         this.reset();
40603         this.el.dom.value = v[this.displayField];
40604         this.hiddenEl.dom.value = v[this.valueField];
40605         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40606             return;
40607         }
40608         var kv = v[this.valueField];
40609         var dv = v[this.displayField];
40610         kv = typeof(kv) != 'string' ? '' : kv;
40611         dv = typeof(dv) != 'string' ? '' : dv;
40612         
40613         
40614         var keys = kv.split(',');
40615         var display = dv.split(',');
40616         for (var i = 0 ; i < keys.length; i++) {
40617             
40618             add = {};
40619             add[this.valueField] = keys[i];
40620             add[this.displayField] = display[i];
40621             this.addItem(add);
40622         }
40623       
40624         
40625     },
40626     
40627     /**
40628      * Validates the combox array value
40629      * @return {Boolean} True if the value is valid, else false
40630      */
40631     validate : function(){
40632         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40633             this.clearInvalid();
40634             return true;
40635         }
40636         return false;
40637     },
40638     
40639     validateValue : function(value){
40640         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40641         
40642     },
40643     
40644     /*@
40645      * overide
40646      * 
40647      */
40648     isDirty : function() {
40649         if(this.disabled) {
40650             return false;
40651         }
40652         
40653         try {
40654             var d = Roo.decode(String(this.originalValue));
40655         } catch (e) {
40656             return String(this.getValue()) !== String(this.originalValue);
40657         }
40658         
40659         var originalValue = [];
40660         
40661         for (var i = 0; i < d.length; i++){
40662             originalValue.push(d[i][this.valueField]);
40663         }
40664         
40665         return String(this.getValue()) !== String(originalValue.join(','));
40666         
40667     }
40668     
40669 });
40670
40671
40672
40673 /**
40674  * @class Roo.form.ComboBoxArray.Item
40675  * @extends Roo.BoxComponent
40676  * A selected item in the list
40677  *  Fred [x]  Brian [x]  [Pick another |v]
40678  * 
40679  * @constructor
40680  * Create a new item.
40681  * @param {Object} config Configuration options
40682  */
40683  
40684 Roo.form.ComboBoxArray.Item = function(config) {
40685     config.id = Roo.id();
40686     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40687 }
40688
40689 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40690     data : {},
40691     cb: false,
40692     displayField : false,
40693     tipField : false,
40694     
40695     
40696     defaultAutoCreate : {
40697         tag: 'div',
40698         cls: 'x-cbarray-item',
40699         cn : [ 
40700             { tag: 'div' },
40701             {
40702                 tag: 'img',
40703                 width:16,
40704                 height : 16,
40705                 src : Roo.BLANK_IMAGE_URL ,
40706                 align: 'center'
40707             }
40708         ]
40709         
40710     },
40711     
40712  
40713     onRender : function(ct, position)
40714     {
40715         Roo.form.Field.superclass.onRender.call(this, ct, position);
40716         
40717         if(!this.el){
40718             var cfg = this.getAutoCreate();
40719             this.el = ct.createChild(cfg, position);
40720         }
40721         
40722         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40723         
40724         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40725             this.cb.renderer(this.data) :
40726             String.format('{0}',this.data[this.displayField]);
40727         
40728             
40729         this.el.child('div').dom.setAttribute('qtip',
40730                         String.format('{0}',this.data[this.tipField])
40731         );
40732         
40733         this.el.child('img').on('click', this.remove, this);
40734         
40735     },
40736    
40737     remove : function()
40738     {
40739         if(this.cb.disabled){
40740             return;
40741         }
40742         this.cb.items.remove(this);
40743         this.el.child('img').un('click', this.remove, this);
40744         this.el.remove();
40745         this.cb.updateHiddenEl();
40746         
40747         this.cb.fireEvent('remove', this.cb, this);
40748     }
40749 });/*
40750  * Based on:
40751  * Ext JS Library 1.1.1
40752  * Copyright(c) 2006-2007, Ext JS, LLC.
40753  *
40754  * Originally Released Under LGPL - original licence link has changed is not relivant.
40755  *
40756  * Fork - LGPL
40757  * <script type="text/javascript">
40758  */
40759 /**
40760  * @class Roo.form.Checkbox
40761  * @extends Roo.form.Field
40762  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40763  * @constructor
40764  * Creates a new Checkbox
40765  * @param {Object} config Configuration options
40766  */
40767 Roo.form.Checkbox = function(config){
40768     Roo.form.Checkbox.superclass.constructor.call(this, config);
40769     this.addEvents({
40770         /**
40771          * @event check
40772          * Fires when the checkbox is checked or unchecked.
40773              * @param {Roo.form.Checkbox} this This checkbox
40774              * @param {Boolean} checked The new checked value
40775              */
40776         check : true
40777     });
40778 };
40779
40780 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40781     /**
40782      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40783      */
40784     focusClass : undefined,
40785     /**
40786      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40787      */
40788     fieldClass: "x-form-field",
40789     /**
40790      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40791      */
40792     checked: false,
40793     /**
40794      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40795      * {tag: "input", type: "checkbox", autocomplete: "off"})
40796      */
40797     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40798     /**
40799      * @cfg {String} boxLabel The text that appears beside the checkbox
40800      */
40801     boxLabel : "",
40802     /**
40803      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40804      */  
40805     inputValue : '1',
40806     /**
40807      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40808      */
40809      valueOff: '0', // value when not checked..
40810
40811     actionMode : 'viewEl', 
40812     //
40813     // private
40814     itemCls : 'x-menu-check-item x-form-item',
40815     groupClass : 'x-menu-group-item',
40816     inputType : 'hidden',
40817     
40818     
40819     inSetChecked: false, // check that we are not calling self...
40820     
40821     inputElement: false, // real input element?
40822     basedOn: false, // ????
40823     
40824     isFormField: true, // not sure where this is needed!!!!
40825
40826     onResize : function(){
40827         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40828         if(!this.boxLabel){
40829             this.el.alignTo(this.wrap, 'c-c');
40830         }
40831     },
40832
40833     initEvents : function(){
40834         Roo.form.Checkbox.superclass.initEvents.call(this);
40835         this.el.on("click", this.onClick,  this);
40836         this.el.on("change", this.onClick,  this);
40837     },
40838
40839
40840     getResizeEl : function(){
40841         return this.wrap;
40842     },
40843
40844     getPositionEl : function(){
40845         return this.wrap;
40846     },
40847
40848     // private
40849     onRender : function(ct, position){
40850         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40851         /*
40852         if(this.inputValue !== undefined){
40853             this.el.dom.value = this.inputValue;
40854         }
40855         */
40856         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40857         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40858         var viewEl = this.wrap.createChild({ 
40859             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40860         this.viewEl = viewEl;   
40861         this.wrap.on('click', this.onClick,  this); 
40862         
40863         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40864         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40865         
40866         
40867         
40868         if(this.boxLabel){
40869             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40870         //    viewEl.on('click', this.onClick,  this); 
40871         }
40872         //if(this.checked){
40873             this.setChecked(this.checked);
40874         //}else{
40875             //this.checked = this.el.dom;
40876         //}
40877
40878     },
40879
40880     // private
40881     initValue : Roo.emptyFn,
40882
40883     /**
40884      * Returns the checked state of the checkbox.
40885      * @return {Boolean} True if checked, else false
40886      */
40887     getValue : function(){
40888         if(this.el){
40889             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40890         }
40891         return this.valueOff;
40892         
40893     },
40894
40895         // private
40896     onClick : function(){ 
40897         if (this.disabled) {
40898             return;
40899         }
40900         this.setChecked(!this.checked);
40901
40902         //if(this.el.dom.checked != this.checked){
40903         //    this.setValue(this.el.dom.checked);
40904        // }
40905     },
40906
40907     /**
40908      * Sets the checked state of the checkbox.
40909      * On is always based on a string comparison between inputValue and the param.
40910      * @param {Boolean/String} value - the value to set 
40911      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40912      */
40913     setValue : function(v,suppressEvent){
40914         
40915         
40916         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40917         //if(this.el && this.el.dom){
40918         //    this.el.dom.checked = this.checked;
40919         //    this.el.dom.defaultChecked = this.checked;
40920         //}
40921         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40922         //this.fireEvent("check", this, this.checked);
40923     },
40924     // private..
40925     setChecked : function(state,suppressEvent)
40926     {
40927         if (this.inSetChecked) {
40928             this.checked = state;
40929             return;
40930         }
40931         
40932     
40933         if(this.wrap){
40934             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40935         }
40936         this.checked = state;
40937         if(suppressEvent !== true){
40938             this.fireEvent('check', this, state);
40939         }
40940         this.inSetChecked = true;
40941         this.el.dom.value = state ? this.inputValue : this.valueOff;
40942         this.inSetChecked = false;
40943         
40944     },
40945     // handle setting of hidden value by some other method!!?!?
40946     setFromHidden: function()
40947     {
40948         if(!this.el){
40949             return;
40950         }
40951         //console.log("SET FROM HIDDEN");
40952         //alert('setFrom hidden');
40953         this.setValue(this.el.dom.value);
40954     },
40955     
40956     onDestroy : function()
40957     {
40958         if(this.viewEl){
40959             Roo.get(this.viewEl).remove();
40960         }
40961          
40962         Roo.form.Checkbox.superclass.onDestroy.call(this);
40963     }
40964
40965 });/*
40966  * Based on:
40967  * Ext JS Library 1.1.1
40968  * Copyright(c) 2006-2007, Ext JS, LLC.
40969  *
40970  * Originally Released Under LGPL - original licence link has changed is not relivant.
40971  *
40972  * Fork - LGPL
40973  * <script type="text/javascript">
40974  */
40975  
40976 /**
40977  * @class Roo.form.Radio
40978  * @extends Roo.form.Checkbox
40979  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40980  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40981  * @constructor
40982  * Creates a new Radio
40983  * @param {Object} config Configuration options
40984  */
40985 Roo.form.Radio = function(){
40986     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40987 };
40988 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40989     inputType: 'radio',
40990
40991     /**
40992      * If this radio is part of a group, it will return the selected value
40993      * @return {String}
40994      */
40995     getGroupValue : function(){
40996         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40997     },
40998     
40999     
41000     onRender : function(ct, position){
41001         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41002         
41003         if(this.inputValue !== undefined){
41004             this.el.dom.value = this.inputValue;
41005         }
41006          
41007         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41008         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41009         //var viewEl = this.wrap.createChild({ 
41010         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41011         //this.viewEl = viewEl;   
41012         //this.wrap.on('click', this.onClick,  this); 
41013         
41014         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41015         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41016         
41017         
41018         
41019         if(this.boxLabel){
41020             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41021         //    viewEl.on('click', this.onClick,  this); 
41022         }
41023          if(this.checked){
41024             this.el.dom.checked =   'checked' ;
41025         }
41026          
41027     } 
41028     
41029     
41030 });//<script type="text/javascript">
41031
41032 /*
41033  * Based  Ext JS Library 1.1.1
41034  * Copyright(c) 2006-2007, Ext JS, LLC.
41035  * LGPL
41036  *
41037  */
41038  
41039 /**
41040  * @class Roo.HtmlEditorCore
41041  * @extends Roo.Component
41042  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41043  *
41044  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41045  */
41046
41047 Roo.HtmlEditorCore = function(config){
41048     
41049     
41050     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41051     
41052     
41053     this.addEvents({
41054         /**
41055          * @event initialize
41056          * Fires when the editor is fully initialized (including the iframe)
41057          * @param {Roo.HtmlEditorCore} this
41058          */
41059         initialize: true,
41060         /**
41061          * @event activate
41062          * Fires when the editor is first receives the focus. Any insertion must wait
41063          * until after this event.
41064          * @param {Roo.HtmlEditorCore} this
41065          */
41066         activate: true,
41067          /**
41068          * @event beforesync
41069          * Fires before the textarea is updated with content from the editor iframe. Return false
41070          * to cancel the sync.
41071          * @param {Roo.HtmlEditorCore} this
41072          * @param {String} html
41073          */
41074         beforesync: true,
41075          /**
41076          * @event beforepush
41077          * Fires before the iframe editor is updated with content from the textarea. Return false
41078          * to cancel the push.
41079          * @param {Roo.HtmlEditorCore} this
41080          * @param {String} html
41081          */
41082         beforepush: true,
41083          /**
41084          * @event sync
41085          * Fires when the textarea is updated with content from the editor iframe.
41086          * @param {Roo.HtmlEditorCore} this
41087          * @param {String} html
41088          */
41089         sync: true,
41090          /**
41091          * @event push
41092          * Fires when the iframe editor is updated with content from the textarea.
41093          * @param {Roo.HtmlEditorCore} this
41094          * @param {String} html
41095          */
41096         push: true,
41097         
41098         /**
41099          * @event editorevent
41100          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41101          * @param {Roo.HtmlEditorCore} this
41102          */
41103         editorevent: true
41104     });
41105     
41106     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41107     
41108     // defaults : white / black...
41109     this.applyBlacklists();
41110     
41111     
41112     
41113 };
41114
41115
41116 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41117
41118
41119      /**
41120      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41121      */
41122     
41123     owner : false,
41124     
41125      /**
41126      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41127      *                        Roo.resizable.
41128      */
41129     resizable : false,
41130      /**
41131      * @cfg {Number} height (in pixels)
41132      */   
41133     height: 300,
41134    /**
41135      * @cfg {Number} width (in pixels)
41136      */   
41137     width: 500,
41138     
41139     /**
41140      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41141      * 
41142      */
41143     stylesheets: false,
41144     
41145     // id of frame..
41146     frameId: false,
41147     
41148     // private properties
41149     validationEvent : false,
41150     deferHeight: true,
41151     initialized : false,
41152     activated : false,
41153     sourceEditMode : false,
41154     onFocus : Roo.emptyFn,
41155     iframePad:3,
41156     hideMode:'offsets',
41157     
41158     clearUp: true,
41159     
41160     // blacklist + whitelisted elements..
41161     black: false,
41162     white: false,
41163      
41164     
41165
41166     /**
41167      * Protected method that will not generally be called directly. It
41168      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41169      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41170      */
41171     getDocMarkup : function(){
41172         // body styles..
41173         var st = '';
41174         Roo.log(this.stylesheets);
41175         
41176         // inherit styels from page...?? 
41177         if (this.stylesheets === false) {
41178             
41179             Roo.get(document.head).select('style').each(function(node) {
41180                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41181             });
41182             
41183             Roo.get(document.head).select('link').each(function(node) { 
41184                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41185             });
41186             
41187         } else if (!this.stylesheets.length) {
41188                 // simple..
41189                 st = '<style type="text/css">' +
41190                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41191                    '</style>';
41192         } else {
41193             Roo.each(this.stylesheets, function(s) {
41194                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41195             });
41196             
41197         }
41198         
41199         st +=  '<style type="text/css">' +
41200             'IMG { cursor: pointer } ' +
41201         '</style>';
41202
41203         
41204         return '<html><head>' + st  +
41205             //<style type="text/css">' +
41206             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41207             //'</style>' +
41208             ' </head><body class="roo-htmleditor-body"></body></html>';
41209     },
41210
41211     // private
41212     onRender : function(ct, position)
41213     {
41214         var _t = this;
41215         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41216         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41217         
41218         
41219         this.el.dom.style.border = '0 none';
41220         this.el.dom.setAttribute('tabIndex', -1);
41221         this.el.addClass('x-hidden hide');
41222         
41223         
41224         
41225         if(Roo.isIE){ // fix IE 1px bogus margin
41226             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41227         }
41228        
41229         
41230         this.frameId = Roo.id();
41231         
41232          
41233         
41234         var iframe = this.owner.wrap.createChild({
41235             tag: 'iframe',
41236             cls: 'form-control', // bootstrap..
41237             id: this.frameId,
41238             name: this.frameId,
41239             frameBorder : 'no',
41240             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41241         }, this.el
41242         );
41243         
41244         
41245         this.iframe = iframe.dom;
41246
41247          this.assignDocWin();
41248         
41249         this.doc.designMode = 'on';
41250        
41251         this.doc.open();
41252         this.doc.write(this.getDocMarkup());
41253         this.doc.close();
41254
41255         
41256         var task = { // must defer to wait for browser to be ready
41257             run : function(){
41258                 //console.log("run task?" + this.doc.readyState);
41259                 this.assignDocWin();
41260                 if(this.doc.body || this.doc.readyState == 'complete'){
41261                     try {
41262                         this.doc.designMode="on";
41263                     } catch (e) {
41264                         return;
41265                     }
41266                     Roo.TaskMgr.stop(task);
41267                     this.initEditor.defer(10, this);
41268                 }
41269             },
41270             interval : 10,
41271             duration: 10000,
41272             scope: this
41273         };
41274         Roo.TaskMgr.start(task);
41275
41276         
41277          
41278     },
41279
41280     // private
41281     onResize : function(w, h)
41282     {
41283          Roo.log('resize: ' +w + ',' + h );
41284         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41285         if(!this.iframe){
41286             return;
41287         }
41288         if(typeof w == 'number'){
41289             
41290             this.iframe.style.width = w + 'px';
41291         }
41292         if(typeof h == 'number'){
41293             
41294             this.iframe.style.height = h + 'px';
41295             if(this.doc){
41296                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41297             }
41298         }
41299         
41300     },
41301
41302     /**
41303      * Toggles the editor between standard and source edit mode.
41304      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41305      */
41306     toggleSourceEdit : function(sourceEditMode){
41307         
41308         this.sourceEditMode = sourceEditMode === true;
41309         
41310         if(this.sourceEditMode){
41311  
41312             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41313             
41314         }else{
41315             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41316             //this.iframe.className = '';
41317             this.deferFocus();
41318         }
41319         //this.setSize(this.owner.wrap.getSize());
41320         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41321     },
41322
41323     
41324   
41325
41326     /**
41327      * Protected method that will not generally be called directly. If you need/want
41328      * custom HTML cleanup, this is the method you should override.
41329      * @param {String} html The HTML to be cleaned
41330      * return {String} The cleaned HTML
41331      */
41332     cleanHtml : function(html){
41333         html = String(html);
41334         if(html.length > 5){
41335             if(Roo.isSafari){ // strip safari nonsense
41336                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41337             }
41338         }
41339         if(html == '&nbsp;'){
41340             html = '';
41341         }
41342         return html;
41343     },
41344
41345     /**
41346      * HTML Editor -> Textarea
41347      * Protected method that will not generally be called directly. Syncs the contents
41348      * of the editor iframe with the textarea.
41349      */
41350     syncValue : function(){
41351         if(this.initialized){
41352             var bd = (this.doc.body || this.doc.documentElement);
41353             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41354             var html = bd.innerHTML;
41355             if(Roo.isSafari){
41356                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41357                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41358                 if(m && m[1]){
41359                     html = '<div style="'+m[0]+'">' + html + '</div>';
41360                 }
41361             }
41362             html = this.cleanHtml(html);
41363             // fix up the special chars.. normaly like back quotes in word...
41364             // however we do not want to do this with chinese..
41365             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41366                 var cc = b.charCodeAt();
41367                 if (
41368                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41369                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41370                     (cc >= 0xf900 && cc < 0xfb00 )
41371                 ) {
41372                         return b;
41373                 }
41374                 return "&#"+cc+";" 
41375             });
41376             if(this.owner.fireEvent('beforesync', this, html) !== false){
41377                 this.el.dom.value = html;
41378                 this.owner.fireEvent('sync', this, html);
41379             }
41380         }
41381     },
41382
41383     /**
41384      * Protected method that will not generally be called directly. Pushes the value of the textarea
41385      * into the iframe editor.
41386      */
41387     pushValue : function(){
41388         if(this.initialized){
41389             var v = this.el.dom.value.trim();
41390             
41391 //            if(v.length < 1){
41392 //                v = '&#160;';
41393 //            }
41394             
41395             if(this.owner.fireEvent('beforepush', this, v) !== false){
41396                 var d = (this.doc.body || this.doc.documentElement);
41397                 d.innerHTML = v;
41398                 this.cleanUpPaste();
41399                 this.el.dom.value = d.innerHTML;
41400                 this.owner.fireEvent('push', this, v);
41401             }
41402         }
41403     },
41404
41405     // private
41406     deferFocus : function(){
41407         this.focus.defer(10, this);
41408     },
41409
41410     // doc'ed in Field
41411     focus : function(){
41412         if(this.win && !this.sourceEditMode){
41413             this.win.focus();
41414         }else{
41415             this.el.focus();
41416         }
41417     },
41418     
41419     assignDocWin: function()
41420     {
41421         var iframe = this.iframe;
41422         
41423          if(Roo.isIE){
41424             this.doc = iframe.contentWindow.document;
41425             this.win = iframe.contentWindow;
41426         } else {
41427 //            if (!Roo.get(this.frameId)) {
41428 //                return;
41429 //            }
41430 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41431 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41432             
41433             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41434                 return;
41435             }
41436             
41437             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41438             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41439         }
41440     },
41441     
41442     // private
41443     initEditor : function(){
41444         //console.log("INIT EDITOR");
41445         this.assignDocWin();
41446         
41447         
41448         
41449         this.doc.designMode="on";
41450         this.doc.open();
41451         this.doc.write(this.getDocMarkup());
41452         this.doc.close();
41453         
41454         var dbody = (this.doc.body || this.doc.documentElement);
41455         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41456         // this copies styles from the containing element into thsi one..
41457         // not sure why we need all of this..
41458         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41459         
41460         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41461         //ss['background-attachment'] = 'fixed'; // w3c
41462         dbody.bgProperties = 'fixed'; // ie
41463         //Roo.DomHelper.applyStyles(dbody, ss);
41464         Roo.EventManager.on(this.doc, {
41465             //'mousedown': this.onEditorEvent,
41466             'mouseup': this.onEditorEvent,
41467             'dblclick': this.onEditorEvent,
41468             'click': this.onEditorEvent,
41469             'keyup': this.onEditorEvent,
41470             buffer:100,
41471             scope: this
41472         });
41473         if(Roo.isGecko){
41474             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41475         }
41476         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41477             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41478         }
41479         this.initialized = true;
41480
41481         this.owner.fireEvent('initialize', this);
41482         this.pushValue();
41483     },
41484
41485     // private
41486     onDestroy : function(){
41487         
41488         
41489         
41490         if(this.rendered){
41491             
41492             //for (var i =0; i < this.toolbars.length;i++) {
41493             //    // fixme - ask toolbars for heights?
41494             //    this.toolbars[i].onDestroy();
41495            // }
41496             
41497             //this.wrap.dom.innerHTML = '';
41498             //this.wrap.remove();
41499         }
41500     },
41501
41502     // private
41503     onFirstFocus : function(){
41504         
41505         this.assignDocWin();
41506         
41507         
41508         this.activated = true;
41509          
41510     
41511         if(Roo.isGecko){ // prevent silly gecko errors
41512             this.win.focus();
41513             var s = this.win.getSelection();
41514             if(!s.focusNode || s.focusNode.nodeType != 3){
41515                 var r = s.getRangeAt(0);
41516                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41517                 r.collapse(true);
41518                 this.deferFocus();
41519             }
41520             try{
41521                 this.execCmd('useCSS', true);
41522                 this.execCmd('styleWithCSS', false);
41523             }catch(e){}
41524         }
41525         this.owner.fireEvent('activate', this);
41526     },
41527
41528     // private
41529     adjustFont: function(btn){
41530         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41531         //if(Roo.isSafari){ // safari
41532         //    adjust *= 2;
41533        // }
41534         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41535         if(Roo.isSafari){ // safari
41536             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41537             v =  (v < 10) ? 10 : v;
41538             v =  (v > 48) ? 48 : v;
41539             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41540             
41541         }
41542         
41543         
41544         v = Math.max(1, v+adjust);
41545         
41546         this.execCmd('FontSize', v  );
41547     },
41548
41549     onEditorEvent : function(e){
41550         this.owner.fireEvent('editorevent', this, e);
41551       //  this.updateToolbar();
41552         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41553     },
41554
41555     insertTag : function(tg)
41556     {
41557         // could be a bit smarter... -> wrap the current selected tRoo..
41558         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41559             
41560             range = this.createRange(this.getSelection());
41561             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41562             wrappingNode.appendChild(range.extractContents());
41563             range.insertNode(wrappingNode);
41564
41565             return;
41566             
41567             
41568             
41569         }
41570         this.execCmd("formatblock",   tg);
41571         
41572     },
41573     
41574     insertText : function(txt)
41575     {
41576         
41577         
41578         var range = this.createRange();
41579         range.deleteContents();
41580                //alert(Sender.getAttribute('label'));
41581                
41582         range.insertNode(this.doc.createTextNode(txt));
41583     } ,
41584     
41585      
41586
41587     /**
41588      * Executes a Midas editor command on the editor document and performs necessary focus and
41589      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41590      * @param {String} cmd The Midas command
41591      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41592      */
41593     relayCmd : function(cmd, value){
41594         this.win.focus();
41595         this.execCmd(cmd, value);
41596         this.owner.fireEvent('editorevent', this);
41597         //this.updateToolbar();
41598         this.owner.deferFocus();
41599     },
41600
41601     /**
41602      * Executes a Midas editor command directly on the editor document.
41603      * For visual commands, you should use {@link #relayCmd} instead.
41604      * <b>This should only be called after the editor is initialized.</b>
41605      * @param {String} cmd The Midas command
41606      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41607      */
41608     execCmd : function(cmd, value){
41609         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41610         this.syncValue();
41611     },
41612  
41613  
41614    
41615     /**
41616      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41617      * to insert tRoo.
41618      * @param {String} text | dom node.. 
41619      */
41620     insertAtCursor : function(text)
41621     {
41622         
41623         
41624         
41625         if(!this.activated){
41626             return;
41627         }
41628         /*
41629         if(Roo.isIE){
41630             this.win.focus();
41631             var r = this.doc.selection.createRange();
41632             if(r){
41633                 r.collapse(true);
41634                 r.pasteHTML(text);
41635                 this.syncValue();
41636                 this.deferFocus();
41637             
41638             }
41639             return;
41640         }
41641         */
41642         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41643             this.win.focus();
41644             
41645             
41646             // from jquery ui (MIT licenced)
41647             var range, node;
41648             var win = this.win;
41649             
41650             if (win.getSelection && win.getSelection().getRangeAt) {
41651                 range = win.getSelection().getRangeAt(0);
41652                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41653                 range.insertNode(node);
41654             } else if (win.document.selection && win.document.selection.createRange) {
41655                 // no firefox support
41656                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41657                 win.document.selection.createRange().pasteHTML(txt);
41658             } else {
41659                 // no firefox support
41660                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41661                 this.execCmd('InsertHTML', txt);
41662             } 
41663             
41664             this.syncValue();
41665             
41666             this.deferFocus();
41667         }
41668     },
41669  // private
41670     mozKeyPress : function(e){
41671         if(e.ctrlKey){
41672             var c = e.getCharCode(), cmd;
41673           
41674             if(c > 0){
41675                 c = String.fromCharCode(c).toLowerCase();
41676                 switch(c){
41677                     case 'b':
41678                         cmd = 'bold';
41679                         break;
41680                     case 'i':
41681                         cmd = 'italic';
41682                         break;
41683                     
41684                     case 'u':
41685                         cmd = 'underline';
41686                         break;
41687                     
41688                     case 'v':
41689                         this.cleanUpPaste.defer(100, this);
41690                         return;
41691                         
41692                 }
41693                 if(cmd){
41694                     this.win.focus();
41695                     this.execCmd(cmd);
41696                     this.deferFocus();
41697                     e.preventDefault();
41698                 }
41699                 
41700             }
41701         }
41702     },
41703
41704     // private
41705     fixKeys : function(){ // load time branching for fastest keydown performance
41706         if(Roo.isIE){
41707             return function(e){
41708                 var k = e.getKey(), r;
41709                 if(k == e.TAB){
41710                     e.stopEvent();
41711                     r = this.doc.selection.createRange();
41712                     if(r){
41713                         r.collapse(true);
41714                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41715                         this.deferFocus();
41716                     }
41717                     return;
41718                 }
41719                 
41720                 if(k == e.ENTER){
41721                     r = this.doc.selection.createRange();
41722                     if(r){
41723                         var target = r.parentElement();
41724                         if(!target || target.tagName.toLowerCase() != 'li'){
41725                             e.stopEvent();
41726                             r.pasteHTML('<br />');
41727                             r.collapse(false);
41728                             r.select();
41729                         }
41730                     }
41731                 }
41732                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41733                     this.cleanUpPaste.defer(100, this);
41734                     return;
41735                 }
41736                 
41737                 
41738             };
41739         }else if(Roo.isOpera){
41740             return function(e){
41741                 var k = e.getKey();
41742                 if(k == e.TAB){
41743                     e.stopEvent();
41744                     this.win.focus();
41745                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41746                     this.deferFocus();
41747                 }
41748                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41749                     this.cleanUpPaste.defer(100, this);
41750                     return;
41751                 }
41752                 
41753             };
41754         }else if(Roo.isSafari){
41755             return function(e){
41756                 var k = e.getKey();
41757                 
41758                 if(k == e.TAB){
41759                     e.stopEvent();
41760                     this.execCmd('InsertText','\t');
41761                     this.deferFocus();
41762                     return;
41763                 }
41764                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41765                     this.cleanUpPaste.defer(100, this);
41766                     return;
41767                 }
41768                 
41769              };
41770         }
41771     }(),
41772     
41773     getAllAncestors: function()
41774     {
41775         var p = this.getSelectedNode();
41776         var a = [];
41777         if (!p) {
41778             a.push(p); // push blank onto stack..
41779             p = this.getParentElement();
41780         }
41781         
41782         
41783         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41784             a.push(p);
41785             p = p.parentNode;
41786         }
41787         a.push(this.doc.body);
41788         return a;
41789     },
41790     lastSel : false,
41791     lastSelNode : false,
41792     
41793     
41794     getSelection : function() 
41795     {
41796         this.assignDocWin();
41797         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41798     },
41799     
41800     getSelectedNode: function() 
41801     {
41802         // this may only work on Gecko!!!
41803         
41804         // should we cache this!!!!
41805         
41806         
41807         
41808          
41809         var range = this.createRange(this.getSelection()).cloneRange();
41810         
41811         if (Roo.isIE) {
41812             var parent = range.parentElement();
41813             while (true) {
41814                 var testRange = range.duplicate();
41815                 testRange.moveToElementText(parent);
41816                 if (testRange.inRange(range)) {
41817                     break;
41818                 }
41819                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41820                     break;
41821                 }
41822                 parent = parent.parentElement;
41823             }
41824             return parent;
41825         }
41826         
41827         // is ancestor a text element.
41828         var ac =  range.commonAncestorContainer;
41829         if (ac.nodeType == 3) {
41830             ac = ac.parentNode;
41831         }
41832         
41833         var ar = ac.childNodes;
41834          
41835         var nodes = [];
41836         var other_nodes = [];
41837         var has_other_nodes = false;
41838         for (var i=0;i<ar.length;i++) {
41839             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41840                 continue;
41841             }
41842             // fullly contained node.
41843             
41844             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41845                 nodes.push(ar[i]);
41846                 continue;
41847             }
41848             
41849             // probably selected..
41850             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41851                 other_nodes.push(ar[i]);
41852                 continue;
41853             }
41854             // outer..
41855             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41856                 continue;
41857             }
41858             
41859             
41860             has_other_nodes = true;
41861         }
41862         if (!nodes.length && other_nodes.length) {
41863             nodes= other_nodes;
41864         }
41865         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41866             return false;
41867         }
41868         
41869         return nodes[0];
41870     },
41871     createRange: function(sel)
41872     {
41873         // this has strange effects when using with 
41874         // top toolbar - not sure if it's a great idea.
41875         //this.editor.contentWindow.focus();
41876         if (typeof sel != "undefined") {
41877             try {
41878                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41879             } catch(e) {
41880                 return this.doc.createRange();
41881             }
41882         } else {
41883             return this.doc.createRange();
41884         }
41885     },
41886     getParentElement: function()
41887     {
41888         
41889         this.assignDocWin();
41890         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41891         
41892         var range = this.createRange(sel);
41893          
41894         try {
41895             var p = range.commonAncestorContainer;
41896             while (p.nodeType == 3) { // text node
41897                 p = p.parentNode;
41898             }
41899             return p;
41900         } catch (e) {
41901             return null;
41902         }
41903     
41904     },
41905     /***
41906      *
41907      * Range intersection.. the hard stuff...
41908      *  '-1' = before
41909      *  '0' = hits..
41910      *  '1' = after.
41911      *         [ -- selected range --- ]
41912      *   [fail]                        [fail]
41913      *
41914      *    basically..
41915      *      if end is before start or  hits it. fail.
41916      *      if start is after end or hits it fail.
41917      *
41918      *   if either hits (but other is outside. - then it's not 
41919      *   
41920      *    
41921      **/
41922     
41923     
41924     // @see http://www.thismuchiknow.co.uk/?p=64.
41925     rangeIntersectsNode : function(range, node)
41926     {
41927         var nodeRange = node.ownerDocument.createRange();
41928         try {
41929             nodeRange.selectNode(node);
41930         } catch (e) {
41931             nodeRange.selectNodeContents(node);
41932         }
41933     
41934         var rangeStartRange = range.cloneRange();
41935         rangeStartRange.collapse(true);
41936     
41937         var rangeEndRange = range.cloneRange();
41938         rangeEndRange.collapse(false);
41939     
41940         var nodeStartRange = nodeRange.cloneRange();
41941         nodeStartRange.collapse(true);
41942     
41943         var nodeEndRange = nodeRange.cloneRange();
41944         nodeEndRange.collapse(false);
41945     
41946         return rangeStartRange.compareBoundaryPoints(
41947                  Range.START_TO_START, nodeEndRange) == -1 &&
41948                rangeEndRange.compareBoundaryPoints(
41949                  Range.START_TO_START, nodeStartRange) == 1;
41950         
41951          
41952     },
41953     rangeCompareNode : function(range, node)
41954     {
41955         var nodeRange = node.ownerDocument.createRange();
41956         try {
41957             nodeRange.selectNode(node);
41958         } catch (e) {
41959             nodeRange.selectNodeContents(node);
41960         }
41961         
41962         
41963         range.collapse(true);
41964     
41965         nodeRange.collapse(true);
41966      
41967         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41968         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41969          
41970         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41971         
41972         var nodeIsBefore   =  ss == 1;
41973         var nodeIsAfter    = ee == -1;
41974         
41975         if (nodeIsBefore && nodeIsAfter)
41976             return 0; // outer
41977         if (!nodeIsBefore && nodeIsAfter)
41978             return 1; //right trailed.
41979         
41980         if (nodeIsBefore && !nodeIsAfter)
41981             return 2;  // left trailed.
41982         // fully contined.
41983         return 3;
41984     },
41985
41986     // private? - in a new class?
41987     cleanUpPaste :  function()
41988     {
41989         // cleans up the whole document..
41990         Roo.log('cleanuppaste');
41991         
41992         this.cleanUpChildren(this.doc.body);
41993         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41994         if (clean != this.doc.body.innerHTML) {
41995             this.doc.body.innerHTML = clean;
41996         }
41997         
41998     },
41999     
42000     cleanWordChars : function(input) {// change the chars to hex code
42001         var he = Roo.HtmlEditorCore;
42002         
42003         var output = input;
42004         Roo.each(he.swapCodes, function(sw) { 
42005             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42006             
42007             output = output.replace(swapper, sw[1]);
42008         });
42009         
42010         return output;
42011     },
42012     
42013     
42014     cleanUpChildren : function (n)
42015     {
42016         if (!n.childNodes.length) {
42017             return;
42018         }
42019         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42020            this.cleanUpChild(n.childNodes[i]);
42021         }
42022     },
42023     
42024     
42025         
42026     
42027     cleanUpChild : function (node)
42028     {
42029         var ed = this;
42030         //console.log(node);
42031         if (node.nodeName == "#text") {
42032             // clean up silly Windows -- stuff?
42033             return; 
42034         }
42035         if (node.nodeName == "#comment") {
42036             node.parentNode.removeChild(node);
42037             // clean up silly Windows -- stuff?
42038             return; 
42039         }
42040         var lcname = node.tagName.toLowerCase();
42041         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42042         // whitelist of tags..
42043         
42044         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42045             // remove node.
42046             node.parentNode.removeChild(node);
42047             return;
42048             
42049         }
42050         
42051         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42052         
42053         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42054         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42055         
42056         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42057         //    remove_keep_children = true;
42058         //}
42059         
42060         if (remove_keep_children) {
42061             this.cleanUpChildren(node);
42062             // inserts everything just before this node...
42063             while (node.childNodes.length) {
42064                 var cn = node.childNodes[0];
42065                 node.removeChild(cn);
42066                 node.parentNode.insertBefore(cn, node);
42067             }
42068             node.parentNode.removeChild(node);
42069             return;
42070         }
42071         
42072         if (!node.attributes || !node.attributes.length) {
42073             this.cleanUpChildren(node);
42074             return;
42075         }
42076         
42077         function cleanAttr(n,v)
42078         {
42079             
42080             if (v.match(/^\./) || v.match(/^\//)) {
42081                 return;
42082             }
42083             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42084                 return;
42085             }
42086             if (v.match(/^#/)) {
42087                 return;
42088             }
42089 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42090             node.removeAttribute(n);
42091             
42092         }
42093         
42094         var cwhite = this.cwhite;
42095         var cblack = this.cblack;
42096             
42097         function cleanStyle(n,v)
42098         {
42099             if (v.match(/expression/)) { //XSS?? should we even bother..
42100                 node.removeAttribute(n);
42101                 return;
42102             }
42103             
42104             var parts = v.split(/;/);
42105             var clean = [];
42106             
42107             Roo.each(parts, function(p) {
42108                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42109                 if (!p.length) {
42110                     return true;
42111                 }
42112                 var l = p.split(':').shift().replace(/\s+/g,'');
42113                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42114                 
42115                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42116 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42117                     //node.removeAttribute(n);
42118                     return true;
42119                 }
42120                 //Roo.log()
42121                 // only allow 'c whitelisted system attributes'
42122                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42123 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42124                     //node.removeAttribute(n);
42125                     return true;
42126                 }
42127                 
42128                 
42129                  
42130                 
42131                 clean.push(p);
42132                 return true;
42133             });
42134             if (clean.length) { 
42135                 node.setAttribute(n, clean.join(';'));
42136             } else {
42137                 node.removeAttribute(n);
42138             }
42139             
42140         }
42141         
42142         
42143         for (var i = node.attributes.length-1; i > -1 ; i--) {
42144             var a = node.attributes[i];
42145             //console.log(a);
42146             
42147             if (a.name.toLowerCase().substr(0,2)=='on')  {
42148                 node.removeAttribute(a.name);
42149                 continue;
42150             }
42151             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42152                 node.removeAttribute(a.name);
42153                 continue;
42154             }
42155             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42156                 cleanAttr(a.name,a.value); // fixme..
42157                 continue;
42158             }
42159             if (a.name == 'style') {
42160                 cleanStyle(a.name,a.value);
42161                 continue;
42162             }
42163             /// clean up MS crap..
42164             // tecnically this should be a list of valid class'es..
42165             
42166             
42167             if (a.name == 'class') {
42168                 if (a.value.match(/^Mso/)) {
42169                     node.className = '';
42170                 }
42171                 
42172                 if (a.value.match(/body/)) {
42173                     node.className = '';
42174                 }
42175                 continue;
42176             }
42177             
42178             // style cleanup!?
42179             // class cleanup?
42180             
42181         }
42182         
42183         
42184         this.cleanUpChildren(node);
42185         
42186         
42187     },
42188     /**
42189      * Clean up MS wordisms...
42190      */
42191     cleanWord : function(node)
42192     {
42193         var _t = this;
42194         var cleanWordChildren = function()
42195         {
42196             if (!node.childNodes.length) {
42197                 return;
42198             }
42199             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42200                _t.cleanWord(node.childNodes[i]);
42201             }
42202         }
42203         
42204         
42205         if (!node) {
42206             this.cleanWord(this.doc.body);
42207             return;
42208         }
42209         if (node.nodeName == "#text") {
42210             // clean up silly Windows -- stuff?
42211             return; 
42212         }
42213         if (node.nodeName == "#comment") {
42214             node.parentNode.removeChild(node);
42215             // clean up silly Windows -- stuff?
42216             return; 
42217         }
42218         
42219         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42220             node.parentNode.removeChild(node);
42221             return;
42222         }
42223         
42224         // remove - but keep children..
42225         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42226             while (node.childNodes.length) {
42227                 var cn = node.childNodes[0];
42228                 node.removeChild(cn);
42229                 node.parentNode.insertBefore(cn, node);
42230             }
42231             node.parentNode.removeChild(node);
42232             cleanWordChildren();
42233             return;
42234         }
42235         // clean styles
42236         if (node.className.length) {
42237             
42238             var cn = node.className.split(/\W+/);
42239             var cna = [];
42240             Roo.each(cn, function(cls) {
42241                 if (cls.match(/Mso[a-zA-Z]+/)) {
42242                     return;
42243                 }
42244                 cna.push(cls);
42245             });
42246             node.className = cna.length ? cna.join(' ') : '';
42247             if (!cna.length) {
42248                 node.removeAttribute("class");
42249             }
42250         }
42251         
42252         if (node.hasAttribute("lang")) {
42253             node.removeAttribute("lang");
42254         }
42255         
42256         if (node.hasAttribute("style")) {
42257             
42258             var styles = node.getAttribute("style").split(";");
42259             var nstyle = [];
42260             Roo.each(styles, function(s) {
42261                 if (!s.match(/:/)) {
42262                     return;
42263                 }
42264                 var kv = s.split(":");
42265                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42266                     return;
42267                 }
42268                 // what ever is left... we allow.
42269                 nstyle.push(s);
42270             });
42271             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42272             if (!nstyle.length) {
42273                 node.removeAttribute('style');
42274             }
42275         }
42276         
42277         cleanWordChildren();
42278         
42279         
42280     },
42281     domToHTML : function(currentElement, depth, nopadtext) {
42282         
42283         depth = depth || 0;
42284         nopadtext = nopadtext || false;
42285     
42286         if (!currentElement) {
42287             return this.domToHTML(this.doc.body);
42288         }
42289         
42290         //Roo.log(currentElement);
42291         var j;
42292         var allText = false;
42293         var nodeName = currentElement.nodeName;
42294         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42295         
42296         if  (nodeName == '#text') {
42297             return currentElement.nodeValue;
42298         }
42299         
42300         
42301         var ret = '';
42302         if (nodeName != 'BODY') {
42303              
42304             var i = 0;
42305             // Prints the node tagName, such as <A>, <IMG>, etc
42306             if (tagName) {
42307                 var attr = [];
42308                 for(i = 0; i < currentElement.attributes.length;i++) {
42309                     // quoting?
42310                     var aname = currentElement.attributes.item(i).name;
42311                     if (!currentElement.attributes.item(i).value.length) {
42312                         continue;
42313                     }
42314                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42315                 }
42316                 
42317                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42318             } 
42319             else {
42320                 
42321                 // eack
42322             }
42323         } else {
42324             tagName = false;
42325         }
42326         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42327             return ret;
42328         }
42329         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42330             nopadtext = true;
42331         }
42332         
42333         
42334         // Traverse the tree
42335         i = 0;
42336         var currentElementChild = currentElement.childNodes.item(i);
42337         var allText = true;
42338         var innerHTML  = '';
42339         lastnode = '';
42340         while (currentElementChild) {
42341             // Formatting code (indent the tree so it looks nice on the screen)
42342             var nopad = nopadtext;
42343             if (lastnode == 'SPAN') {
42344                 nopad  = true;
42345             }
42346             // text
42347             if  (currentElementChild.nodeName == '#text') {
42348                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42349                 if (!nopad && toadd.length > 80) {
42350                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42351                 }
42352                 innerHTML  += toadd;
42353                 
42354                 i++;
42355                 currentElementChild = currentElement.childNodes.item(i);
42356                 lastNode = '';
42357                 continue;
42358             }
42359             allText = false;
42360             
42361             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42362                 
42363             // Recursively traverse the tree structure of the child node
42364             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42365             lastnode = currentElementChild.nodeName;
42366             i++;
42367             currentElementChild=currentElement.childNodes.item(i);
42368         }
42369         
42370         ret += innerHTML;
42371         
42372         if (!allText) {
42373                 // The remaining code is mostly for formatting the tree
42374             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42375         }
42376         
42377         
42378         if (tagName) {
42379             ret+= "</"+tagName+">";
42380         }
42381         return ret;
42382         
42383     },
42384         
42385     applyBlacklists : function()
42386     {
42387         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42388         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42389         
42390         this.white = [];
42391         this.black = [];
42392         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42393             if (b.indexOf(tag) > -1) {
42394                 return;
42395             }
42396             this.white.push(tag);
42397             
42398         }, this);
42399         
42400         Roo.each(w, function(tag) {
42401             if (b.indexOf(tag) > -1) {
42402                 return;
42403             }
42404             if (this.white.indexOf(tag) > -1) {
42405                 return;
42406             }
42407             this.white.push(tag);
42408             
42409         }, this);
42410         
42411         
42412         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42413             if (w.indexOf(tag) > -1) {
42414                 return;
42415             }
42416             this.black.push(tag);
42417             
42418         }, this);
42419         
42420         Roo.each(b, function(tag) {
42421             if (w.indexOf(tag) > -1) {
42422                 return;
42423             }
42424             if (this.black.indexOf(tag) > -1) {
42425                 return;
42426             }
42427             this.black.push(tag);
42428             
42429         }, this);
42430         
42431         
42432         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42433         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42434         
42435         this.cwhite = [];
42436         this.cblack = [];
42437         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42438             if (b.indexOf(tag) > -1) {
42439                 return;
42440             }
42441             this.cwhite.push(tag);
42442             
42443         }, this);
42444         
42445         Roo.each(w, function(tag) {
42446             if (b.indexOf(tag) > -1) {
42447                 return;
42448             }
42449             if (this.cwhite.indexOf(tag) > -1) {
42450                 return;
42451             }
42452             this.cwhite.push(tag);
42453             
42454         }, this);
42455         
42456         
42457         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42458             if (w.indexOf(tag) > -1) {
42459                 return;
42460             }
42461             this.cblack.push(tag);
42462             
42463         }, this);
42464         
42465         Roo.each(b, function(tag) {
42466             if (w.indexOf(tag) > -1) {
42467                 return;
42468             }
42469             if (this.cblack.indexOf(tag) > -1) {
42470                 return;
42471             }
42472             this.cblack.push(tag);
42473             
42474         }, this);
42475     }
42476     
42477     // hide stuff that is not compatible
42478     /**
42479      * @event blur
42480      * @hide
42481      */
42482     /**
42483      * @event change
42484      * @hide
42485      */
42486     /**
42487      * @event focus
42488      * @hide
42489      */
42490     /**
42491      * @event specialkey
42492      * @hide
42493      */
42494     /**
42495      * @cfg {String} fieldClass @hide
42496      */
42497     /**
42498      * @cfg {String} focusClass @hide
42499      */
42500     /**
42501      * @cfg {String} autoCreate @hide
42502      */
42503     /**
42504      * @cfg {String} inputType @hide
42505      */
42506     /**
42507      * @cfg {String} invalidClass @hide
42508      */
42509     /**
42510      * @cfg {String} invalidText @hide
42511      */
42512     /**
42513      * @cfg {String} msgFx @hide
42514      */
42515     /**
42516      * @cfg {String} validateOnBlur @hide
42517      */
42518 });
42519
42520 Roo.HtmlEditorCore.white = [
42521         'area', 'br', 'img', 'input', 'hr', 'wbr',
42522         
42523        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42524        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42525        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42526        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42527        'table',   'ul',         'xmp', 
42528        
42529        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42530       'thead',   'tr', 
42531      
42532       'dir', 'menu', 'ol', 'ul', 'dl',
42533        
42534       'embed',  'object'
42535 ];
42536
42537
42538 Roo.HtmlEditorCore.black = [
42539     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42540         'applet', // 
42541         'base',   'basefont', 'bgsound', 'blink',  'body', 
42542         'frame',  'frameset', 'head',    'html',   'ilayer', 
42543         'iframe', 'layer',  'link',     'meta',    'object',   
42544         'script', 'style' ,'title',  'xml' // clean later..
42545 ];
42546 Roo.HtmlEditorCore.clean = [
42547     'script', 'style', 'title', 'xml'
42548 ];
42549 Roo.HtmlEditorCore.remove = [
42550     'font'
42551 ];
42552 // attributes..
42553
42554 Roo.HtmlEditorCore.ablack = [
42555     'on'
42556 ];
42557     
42558 Roo.HtmlEditorCore.aclean = [ 
42559     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42560 ];
42561
42562 // protocols..
42563 Roo.HtmlEditorCore.pwhite= [
42564         'http',  'https',  'mailto'
42565 ];
42566
42567 // white listed style attributes.
42568 Roo.HtmlEditorCore.cwhite= [
42569       //  'text-align', /// default is to allow most things..
42570       
42571          
42572 //        'font-size'//??
42573 ];
42574
42575 // black listed style attributes.
42576 Roo.HtmlEditorCore.cblack= [
42577       //  'font-size' -- this can be set by the project 
42578 ];
42579
42580
42581 Roo.HtmlEditorCore.swapCodes   =[ 
42582     [    8211, "--" ], 
42583     [    8212, "--" ], 
42584     [    8216,  "'" ],  
42585     [    8217, "'" ],  
42586     [    8220, '"' ],  
42587     [    8221, '"' ],  
42588     [    8226, "*" ],  
42589     [    8230, "..." ]
42590 ]; 
42591
42592     //<script type="text/javascript">
42593
42594 /*
42595  * Ext JS Library 1.1.1
42596  * Copyright(c) 2006-2007, Ext JS, LLC.
42597  * Licence LGPL
42598  * 
42599  */
42600  
42601  
42602 Roo.form.HtmlEditor = function(config){
42603     
42604     
42605     
42606     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42607     
42608     if (!this.toolbars) {
42609         this.toolbars = [];
42610     }
42611     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42612     
42613     
42614 };
42615
42616 /**
42617  * @class Roo.form.HtmlEditor
42618  * @extends Roo.form.Field
42619  * Provides a lightweight HTML Editor component.
42620  *
42621  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42622  * 
42623  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42624  * supported by this editor.</b><br/><br/>
42625  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42626  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42627  */
42628 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42629     /**
42630      * @cfg {Boolean} clearUp
42631      */
42632     clearUp : true,
42633       /**
42634      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42635      */
42636     toolbars : false,
42637    
42638      /**
42639      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42640      *                        Roo.resizable.
42641      */
42642     resizable : false,
42643      /**
42644      * @cfg {Number} height (in pixels)
42645      */   
42646     height: 300,
42647    /**
42648      * @cfg {Number} width (in pixels)
42649      */   
42650     width: 500,
42651     
42652     /**
42653      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42654      * 
42655      */
42656     stylesheets: false,
42657     
42658     
42659      /**
42660      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42661      * 
42662      */
42663     cblack: false,
42664     /**
42665      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42666      * 
42667      */
42668     cwhite: false,
42669     
42670      /**
42671      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42672      * 
42673      */
42674     black: false,
42675     /**
42676      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42677      * 
42678      */
42679     white: false,
42680     
42681     // id of frame..
42682     frameId: false,
42683     
42684     // private properties
42685     validationEvent : false,
42686     deferHeight: true,
42687     initialized : false,
42688     activated : false,
42689     
42690     onFocus : Roo.emptyFn,
42691     iframePad:3,
42692     hideMode:'offsets',
42693     
42694     actionMode : 'container', // defaults to hiding it...
42695     
42696     defaultAutoCreate : { // modified by initCompnoent..
42697         tag: "textarea",
42698         style:"width:500px;height:300px;",
42699         autocomplete: "off"
42700     },
42701
42702     // private
42703     initComponent : function(){
42704         this.addEvents({
42705             /**
42706              * @event initialize
42707              * Fires when the editor is fully initialized (including the iframe)
42708              * @param {HtmlEditor} this
42709              */
42710             initialize: true,
42711             /**
42712              * @event activate
42713              * Fires when the editor is first receives the focus. Any insertion must wait
42714              * until after this event.
42715              * @param {HtmlEditor} this
42716              */
42717             activate: true,
42718              /**
42719              * @event beforesync
42720              * Fires before the textarea is updated with content from the editor iframe. Return false
42721              * to cancel the sync.
42722              * @param {HtmlEditor} this
42723              * @param {String} html
42724              */
42725             beforesync: true,
42726              /**
42727              * @event beforepush
42728              * Fires before the iframe editor is updated with content from the textarea. Return false
42729              * to cancel the push.
42730              * @param {HtmlEditor} this
42731              * @param {String} html
42732              */
42733             beforepush: true,
42734              /**
42735              * @event sync
42736              * Fires when the textarea is updated with content from the editor iframe.
42737              * @param {HtmlEditor} this
42738              * @param {String} html
42739              */
42740             sync: true,
42741              /**
42742              * @event push
42743              * Fires when the iframe editor is updated with content from the textarea.
42744              * @param {HtmlEditor} this
42745              * @param {String} html
42746              */
42747             push: true,
42748              /**
42749              * @event editmodechange
42750              * Fires when the editor switches edit modes
42751              * @param {HtmlEditor} this
42752              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42753              */
42754             editmodechange: true,
42755             /**
42756              * @event editorevent
42757              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42758              * @param {HtmlEditor} this
42759              */
42760             editorevent: true,
42761             /**
42762              * @event firstfocus
42763              * Fires when on first focus - needed by toolbars..
42764              * @param {HtmlEditor} this
42765              */
42766             firstfocus: true,
42767             /**
42768              * @event autosave
42769              * Auto save the htmlEditor value as a file into Events
42770              * @param {HtmlEditor} this
42771              */
42772             autosave: true,
42773             /**
42774              * @event savedpreview
42775              * preview the saved version of htmlEditor
42776              * @param {HtmlEditor} this
42777              */
42778             savedpreview: true
42779         });
42780         this.defaultAutoCreate =  {
42781             tag: "textarea",
42782             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42783             autocomplete: "off"
42784         };
42785     },
42786
42787     /**
42788      * Protected method that will not generally be called directly. It
42789      * is called when the editor creates its toolbar. Override this method if you need to
42790      * add custom toolbar buttons.
42791      * @param {HtmlEditor} editor
42792      */
42793     createToolbar : function(editor){
42794         Roo.log("create toolbars");
42795         if (!editor.toolbars || !editor.toolbars.length) {
42796             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42797         }
42798         
42799         for (var i =0 ; i < editor.toolbars.length;i++) {
42800             editor.toolbars[i] = Roo.factory(
42801                     typeof(editor.toolbars[i]) == 'string' ?
42802                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42803                 Roo.form.HtmlEditor);
42804             editor.toolbars[i].init(editor);
42805         }
42806          
42807         
42808     },
42809
42810      
42811     // private
42812     onRender : function(ct, position)
42813     {
42814         var _t = this;
42815         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42816         
42817         this.wrap = this.el.wrap({
42818             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42819         });
42820         
42821         this.editorcore.onRender(ct, position);
42822          
42823         if (this.resizable) {
42824             this.resizeEl = new Roo.Resizable(this.wrap, {
42825                 pinned : true,
42826                 wrap: true,
42827                 dynamic : true,
42828                 minHeight : this.height,
42829                 height: this.height,
42830                 handles : this.resizable,
42831                 width: this.width,
42832                 listeners : {
42833                     resize : function(r, w, h) {
42834                         _t.onResize(w,h); // -something
42835                     }
42836                 }
42837             });
42838             
42839         }
42840         this.createToolbar(this);
42841        
42842         
42843         if(!this.width){
42844             this.setSize(this.wrap.getSize());
42845         }
42846         if (this.resizeEl) {
42847             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42848             // should trigger onReize..
42849         }
42850         
42851 //        if(this.autosave && this.w){
42852 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42853 //        }
42854     },
42855
42856     // private
42857     onResize : function(w, h)
42858     {
42859         //Roo.log('resize: ' +w + ',' + h );
42860         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42861         var ew = false;
42862         var eh = false;
42863         
42864         if(this.el ){
42865             if(typeof w == 'number'){
42866                 var aw = w - this.wrap.getFrameWidth('lr');
42867                 this.el.setWidth(this.adjustWidth('textarea', aw));
42868                 ew = aw;
42869             }
42870             if(typeof h == 'number'){
42871                 var tbh = 0;
42872                 for (var i =0; i < this.toolbars.length;i++) {
42873                     // fixme - ask toolbars for heights?
42874                     tbh += this.toolbars[i].tb.el.getHeight();
42875                     if (this.toolbars[i].footer) {
42876                         tbh += this.toolbars[i].footer.el.getHeight();
42877                     }
42878                 }
42879                 
42880                 
42881                 
42882                 
42883                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42884                 ah -= 5; // knock a few pixes off for look..
42885                 this.el.setHeight(this.adjustWidth('textarea', ah));
42886                 var eh = ah;
42887             }
42888         }
42889         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42890         this.editorcore.onResize(ew,eh);
42891         
42892     },
42893
42894     /**
42895      * Toggles the editor between standard and source edit mode.
42896      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42897      */
42898     toggleSourceEdit : function(sourceEditMode)
42899     {
42900         this.editorcore.toggleSourceEdit(sourceEditMode);
42901         
42902         if(this.editorcore.sourceEditMode){
42903             Roo.log('editor - showing textarea');
42904             
42905 //            Roo.log('in');
42906 //            Roo.log(this.syncValue());
42907             this.editorcore.syncValue();
42908             this.el.removeClass('x-hidden');
42909             this.el.dom.removeAttribute('tabIndex');
42910             this.el.focus();
42911         }else{
42912             Roo.log('editor - hiding textarea');
42913 //            Roo.log('out')
42914 //            Roo.log(this.pushValue()); 
42915             this.editorcore.pushValue();
42916             
42917             this.el.addClass('x-hidden');
42918             this.el.dom.setAttribute('tabIndex', -1);
42919             //this.deferFocus();
42920         }
42921          
42922         this.setSize(this.wrap.getSize());
42923         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42924     },
42925  
42926     // private (for BoxComponent)
42927     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42928
42929     // private (for BoxComponent)
42930     getResizeEl : function(){
42931         return this.wrap;
42932     },
42933
42934     // private (for BoxComponent)
42935     getPositionEl : function(){
42936         return this.wrap;
42937     },
42938
42939     // private
42940     initEvents : function(){
42941         this.originalValue = this.getValue();
42942     },
42943
42944     /**
42945      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42946      * @method
42947      */
42948     markInvalid : Roo.emptyFn,
42949     /**
42950      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42951      * @method
42952      */
42953     clearInvalid : Roo.emptyFn,
42954
42955     setValue : function(v){
42956         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42957         this.editorcore.pushValue();
42958     },
42959
42960      
42961     // private
42962     deferFocus : function(){
42963         this.focus.defer(10, this);
42964     },
42965
42966     // doc'ed in Field
42967     focus : function(){
42968         this.editorcore.focus();
42969         
42970     },
42971       
42972
42973     // private
42974     onDestroy : function(){
42975         
42976         
42977         
42978         if(this.rendered){
42979             
42980             for (var i =0; i < this.toolbars.length;i++) {
42981                 // fixme - ask toolbars for heights?
42982                 this.toolbars[i].onDestroy();
42983             }
42984             
42985             this.wrap.dom.innerHTML = '';
42986             this.wrap.remove();
42987         }
42988     },
42989
42990     // private
42991     onFirstFocus : function(){
42992         //Roo.log("onFirstFocus");
42993         this.editorcore.onFirstFocus();
42994          for (var i =0; i < this.toolbars.length;i++) {
42995             this.toolbars[i].onFirstFocus();
42996         }
42997         
42998     },
42999     
43000     // private
43001     syncValue : function()
43002     {
43003         this.editorcore.syncValue();
43004     },
43005     
43006     pushValue : function()
43007     {
43008         this.editorcore.pushValue();
43009     }
43010      
43011     
43012     // hide stuff that is not compatible
43013     /**
43014      * @event blur
43015      * @hide
43016      */
43017     /**
43018      * @event change
43019      * @hide
43020      */
43021     /**
43022      * @event focus
43023      * @hide
43024      */
43025     /**
43026      * @event specialkey
43027      * @hide
43028      */
43029     /**
43030      * @cfg {String} fieldClass @hide
43031      */
43032     /**
43033      * @cfg {String} focusClass @hide
43034      */
43035     /**
43036      * @cfg {String} autoCreate @hide
43037      */
43038     /**
43039      * @cfg {String} inputType @hide
43040      */
43041     /**
43042      * @cfg {String} invalidClass @hide
43043      */
43044     /**
43045      * @cfg {String} invalidText @hide
43046      */
43047     /**
43048      * @cfg {String} msgFx @hide
43049      */
43050     /**
43051      * @cfg {String} validateOnBlur @hide
43052      */
43053 });
43054  
43055     // <script type="text/javascript">
43056 /*
43057  * Based on
43058  * Ext JS Library 1.1.1
43059  * Copyright(c) 2006-2007, Ext JS, LLC.
43060  *  
43061  
43062  */
43063
43064 /**
43065  * @class Roo.form.HtmlEditorToolbar1
43066  * Basic Toolbar
43067  * 
43068  * Usage:
43069  *
43070  new Roo.form.HtmlEditor({
43071     ....
43072     toolbars : [
43073         new Roo.form.HtmlEditorToolbar1({
43074             disable : { fonts: 1 , format: 1, ..., ... , ...],
43075             btns : [ .... ]
43076         })
43077     }
43078      
43079  * 
43080  * @cfg {Object} disable List of elements to disable..
43081  * @cfg {Array} btns List of additional buttons.
43082  * 
43083  * 
43084  * NEEDS Extra CSS? 
43085  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43086  */
43087  
43088 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43089 {
43090     
43091     Roo.apply(this, config);
43092     
43093     // default disabled, based on 'good practice'..
43094     this.disable = this.disable || {};
43095     Roo.applyIf(this.disable, {
43096         fontSize : true,
43097         colors : true,
43098         specialElements : true
43099     });
43100     
43101     
43102     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43103     // dont call parent... till later.
43104 }
43105
43106 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43107     
43108     tb: false,
43109     
43110     rendered: false,
43111     
43112     editor : false,
43113     editorcore : false,
43114     /**
43115      * @cfg {Object} disable  List of toolbar elements to disable
43116          
43117      */
43118     disable : false,
43119     
43120     
43121      /**
43122      * @cfg {String} createLinkText The default text for the create link prompt
43123      */
43124     createLinkText : 'Please enter the URL for the link:',
43125     /**
43126      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43127      */
43128     defaultLinkValue : 'http:/'+'/',
43129    
43130     
43131       /**
43132      * @cfg {Array} fontFamilies An array of available font families
43133      */
43134     fontFamilies : [
43135         'Arial',
43136         'Courier New',
43137         'Tahoma',
43138         'Times New Roman',
43139         'Verdana'
43140     ],
43141     
43142     specialChars : [
43143            "&#169;",
43144           "&#174;",     
43145           "&#8482;",    
43146           "&#163;" ,    
43147          // "&#8212;",    
43148           "&#8230;",    
43149           "&#247;" ,    
43150         //  "&#225;" ,     ?? a acute?
43151            "&#8364;"    , //Euro
43152        //   "&#8220;"    ,
43153         //  "&#8221;"    ,
43154         //  "&#8226;"    ,
43155           "&#176;"  //   , // degrees
43156
43157          // "&#233;"     , // e ecute
43158          // "&#250;"     , // u ecute?
43159     ],
43160     
43161     specialElements : [
43162         {
43163             text: "Insert Table",
43164             xtype: 'MenuItem',
43165             xns : Roo.Menu,
43166             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43167                 
43168         },
43169         {    
43170             text: "Insert Image",
43171             xtype: 'MenuItem',
43172             xns : Roo.Menu,
43173             ihtml : '<img src="about:blank"/>'
43174             
43175         }
43176         
43177          
43178     ],
43179     
43180     
43181     inputElements : [ 
43182             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43183             "input:submit", "input:button", "select", "textarea", "label" ],
43184     formats : [
43185         ["p"] ,  
43186         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43187         ["pre"],[ "code"], 
43188         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43189         ['div'],['span']
43190     ],
43191     
43192     cleanStyles : [
43193         "font-size"
43194     ],
43195      /**
43196      * @cfg {String} defaultFont default font to use.
43197      */
43198     defaultFont: 'tahoma',
43199    
43200     fontSelect : false,
43201     
43202     
43203     formatCombo : false,
43204     
43205     init : function(editor)
43206     {
43207         this.editor = editor;
43208         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43209         var editorcore = this.editorcore;
43210         
43211         var _t = this;
43212         
43213         var fid = editorcore.frameId;
43214         var etb = this;
43215         function btn(id, toggle, handler){
43216             var xid = fid + '-'+ id ;
43217             return {
43218                 id : xid,
43219                 cmd : id,
43220                 cls : 'x-btn-icon x-edit-'+id,
43221                 enableToggle:toggle !== false,
43222                 scope: _t, // was editor...
43223                 handler:handler||_t.relayBtnCmd,
43224                 clickEvent:'mousedown',
43225                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43226                 tabIndex:-1
43227             };
43228         }
43229         
43230         
43231         
43232         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43233         this.tb = tb;
43234          // stop form submits
43235         tb.el.on('click', function(e){
43236             e.preventDefault(); // what does this do?
43237         });
43238
43239         if(!this.disable.font) { // && !Roo.isSafari){
43240             /* why no safari for fonts 
43241             editor.fontSelect = tb.el.createChild({
43242                 tag:'select',
43243                 tabIndex: -1,
43244                 cls:'x-font-select',
43245                 html: this.createFontOptions()
43246             });
43247             
43248             editor.fontSelect.on('change', function(){
43249                 var font = editor.fontSelect.dom.value;
43250                 editor.relayCmd('fontname', font);
43251                 editor.deferFocus();
43252             }, editor);
43253             
43254             tb.add(
43255                 editor.fontSelect.dom,
43256                 '-'
43257             );
43258             */
43259             
43260         };
43261         if(!this.disable.formats){
43262             this.formatCombo = new Roo.form.ComboBox({
43263                 store: new Roo.data.SimpleStore({
43264                     id : 'tag',
43265                     fields: ['tag'],
43266                     data : this.formats // from states.js
43267                 }),
43268                 blockFocus : true,
43269                 name : '',
43270                 //autoCreate : {tag: "div",  size: "20"},
43271                 displayField:'tag',
43272                 typeAhead: false,
43273                 mode: 'local',
43274                 editable : false,
43275                 triggerAction: 'all',
43276                 emptyText:'Add tag',
43277                 selectOnFocus:true,
43278                 width:135,
43279                 listeners : {
43280                     'select': function(c, r, i) {
43281                         editorcore.insertTag(r.get('tag'));
43282                         editor.focus();
43283                     }
43284                 }
43285
43286             });
43287             tb.addField(this.formatCombo);
43288             
43289         }
43290         
43291         if(!this.disable.format){
43292             tb.add(
43293                 btn('bold'),
43294                 btn('italic'),
43295                 btn('underline')
43296             );
43297         };
43298         if(!this.disable.fontSize){
43299             tb.add(
43300                 '-',
43301                 
43302                 
43303                 btn('increasefontsize', false, editorcore.adjustFont),
43304                 btn('decreasefontsize', false, editorcore.adjustFont)
43305             );
43306         };
43307         
43308         
43309         if(!this.disable.colors){
43310             tb.add(
43311                 '-', {
43312                     id:editorcore.frameId +'-forecolor',
43313                     cls:'x-btn-icon x-edit-forecolor',
43314                     clickEvent:'mousedown',
43315                     tooltip: this.buttonTips['forecolor'] || undefined,
43316                     tabIndex:-1,
43317                     menu : new Roo.menu.ColorMenu({
43318                         allowReselect: true,
43319                         focus: Roo.emptyFn,
43320                         value:'000000',
43321                         plain:true,
43322                         selectHandler: function(cp, color){
43323                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43324                             editor.deferFocus();
43325                         },
43326                         scope: editorcore,
43327                         clickEvent:'mousedown'
43328                     })
43329                 }, {
43330                     id:editorcore.frameId +'backcolor',
43331                     cls:'x-btn-icon x-edit-backcolor',
43332                     clickEvent:'mousedown',
43333                     tooltip: this.buttonTips['backcolor'] || undefined,
43334                     tabIndex:-1,
43335                     menu : new Roo.menu.ColorMenu({
43336                         focus: Roo.emptyFn,
43337                         value:'FFFFFF',
43338                         plain:true,
43339                         allowReselect: true,
43340                         selectHandler: function(cp, color){
43341                             if(Roo.isGecko){
43342                                 editorcore.execCmd('useCSS', false);
43343                                 editorcore.execCmd('hilitecolor', color);
43344                                 editorcore.execCmd('useCSS', true);
43345                                 editor.deferFocus();
43346                             }else{
43347                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43348                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43349                                 editor.deferFocus();
43350                             }
43351                         },
43352                         scope:editorcore,
43353                         clickEvent:'mousedown'
43354                     })
43355                 }
43356             );
43357         };
43358         // now add all the items...
43359         
43360
43361         if(!this.disable.alignments){
43362             tb.add(
43363                 '-',
43364                 btn('justifyleft'),
43365                 btn('justifycenter'),
43366                 btn('justifyright')
43367             );
43368         };
43369
43370         //if(!Roo.isSafari){
43371             if(!this.disable.links){
43372                 tb.add(
43373                     '-',
43374                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43375                 );
43376             };
43377
43378             if(!this.disable.lists){
43379                 tb.add(
43380                     '-',
43381                     btn('insertorderedlist'),
43382                     btn('insertunorderedlist')
43383                 );
43384             }
43385             if(!this.disable.sourceEdit){
43386                 tb.add(
43387                     '-',
43388                     btn('sourceedit', true, function(btn){
43389                         Roo.log(this);
43390                         this.toggleSourceEdit(btn.pressed);
43391                     })
43392                 );
43393             }
43394         //}
43395         
43396         var smenu = { };
43397         // special menu.. - needs to be tidied up..
43398         if (!this.disable.special) {
43399             smenu = {
43400                 text: "&#169;",
43401                 cls: 'x-edit-none',
43402                 
43403                 menu : {
43404                     items : []
43405                 }
43406             };
43407             for (var i =0; i < this.specialChars.length; i++) {
43408                 smenu.menu.items.push({
43409                     
43410                     html: this.specialChars[i],
43411                     handler: function(a,b) {
43412                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43413                         //editor.insertAtCursor(a.html);
43414                         
43415                     },
43416                     tabIndex:-1
43417                 });
43418             }
43419             
43420             
43421             tb.add(smenu);
43422             
43423             
43424         }
43425         
43426         var cmenu = { };
43427         if (!this.disable.cleanStyles) {
43428             cmenu = {
43429                 cls: 'x-btn-icon x-btn-clear',
43430                 
43431                 menu : {
43432                     items : []
43433                 }
43434             };
43435             for (var i =0; i < this.cleanStyles.length; i++) {
43436                 cmenu.menu.items.push({
43437                     actiontype : this.cleanStyles[i],
43438                     html: 'Remove ' + this.cleanStyles[i],
43439                     handler: function(a,b) {
43440                         Roo.log(a);
43441                         Roo.log(b);
43442                         var c = Roo.get(editorcore.doc.body);
43443                         c.select('[style]').each(function(s) {
43444                             s.dom.style.removeProperty(a.actiontype);
43445                         });
43446                         editorcore.syncValue();
43447                     },
43448                     tabIndex:-1
43449                 });
43450             }
43451             cmenu.menu.items.push({
43452                 actiontype : 'word',
43453                 html: 'Remove MS Word Formating',
43454                 handler: function(a,b) {
43455                     editorcore.cleanWord();
43456                     editorcore.syncValue();
43457                 },
43458                 tabIndex:-1
43459             });
43460             
43461             cmenu.menu.items.push({
43462                 actiontype : 'all',
43463                 html: 'Remove All Styles',
43464                 handler: function(a,b) {
43465                     
43466                     var c = Roo.get(editorcore.doc.body);
43467                     c.select('[style]').each(function(s) {
43468                         s.dom.removeAttribute('style');
43469                     });
43470                     editorcore.syncValue();
43471                 },
43472                 tabIndex:-1
43473             });
43474              cmenu.menu.items.push({
43475                 actiontype : 'word',
43476                 html: 'Tidy HTML Source',
43477                 handler: function(a,b) {
43478                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43479                     editorcore.syncValue();
43480                 },
43481                 tabIndex:-1
43482             });
43483             
43484             
43485             tb.add(cmenu);
43486         }
43487          
43488         if (!this.disable.specialElements) {
43489             var semenu = {
43490                 text: "Other;",
43491                 cls: 'x-edit-none',
43492                 menu : {
43493                     items : []
43494                 }
43495             };
43496             for (var i =0; i < this.specialElements.length; i++) {
43497                 semenu.menu.items.push(
43498                     Roo.apply({ 
43499                         handler: function(a,b) {
43500                             editor.insertAtCursor(this.ihtml);
43501                         }
43502                     }, this.specialElements[i])
43503                 );
43504                     
43505             }
43506             
43507             tb.add(semenu);
43508             
43509             
43510         }
43511          
43512         
43513         if (this.btns) {
43514             for(var i =0; i< this.btns.length;i++) {
43515                 var b = Roo.factory(this.btns[i],Roo.form);
43516                 b.cls =  'x-edit-none';
43517                 b.scope = editorcore;
43518                 tb.add(b);
43519             }
43520         
43521         }
43522         
43523         
43524         
43525         // disable everything...
43526         
43527         this.tb.items.each(function(item){
43528            if(item.id != editorcore.frameId+ '-sourceedit'){
43529                 item.disable();
43530             }
43531         });
43532         this.rendered = true;
43533         
43534         // the all the btns;
43535         editor.on('editorevent', this.updateToolbar, this);
43536         // other toolbars need to implement this..
43537         //editor.on('editmodechange', this.updateToolbar, this);
43538     },
43539     
43540     
43541     relayBtnCmd : function(btn) {
43542         this.editorcore.relayCmd(btn.cmd);
43543     },
43544     // private used internally
43545     createLink : function(){
43546         Roo.log("create link?");
43547         var url = prompt(this.createLinkText, this.defaultLinkValue);
43548         if(url && url != 'http:/'+'/'){
43549             this.editorcore.relayCmd('createlink', url);
43550         }
43551     },
43552
43553     
43554     /**
43555      * Protected method that will not generally be called directly. It triggers
43556      * a toolbar update by reading the markup state of the current selection in the editor.
43557      */
43558     updateToolbar: function(){
43559
43560         if(!this.editorcore.activated){
43561             this.editor.onFirstFocus();
43562             return;
43563         }
43564
43565         var btns = this.tb.items.map, 
43566             doc = this.editorcore.doc,
43567             frameId = this.editorcore.frameId;
43568
43569         if(!this.disable.font && !Roo.isSafari){
43570             /*
43571             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43572             if(name != this.fontSelect.dom.value){
43573                 this.fontSelect.dom.value = name;
43574             }
43575             */
43576         }
43577         if(!this.disable.format){
43578             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43579             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43580             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43581         }
43582         if(!this.disable.alignments){
43583             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43584             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43585             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43586         }
43587         if(!Roo.isSafari && !this.disable.lists){
43588             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43589             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43590         }
43591         
43592         var ans = this.editorcore.getAllAncestors();
43593         if (this.formatCombo) {
43594             
43595             
43596             var store = this.formatCombo.store;
43597             this.formatCombo.setValue("");
43598             for (var i =0; i < ans.length;i++) {
43599                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43600                     // select it..
43601                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43602                     break;
43603                 }
43604             }
43605         }
43606         
43607         
43608         
43609         // hides menus... - so this cant be on a menu...
43610         Roo.menu.MenuMgr.hideAll();
43611
43612         //this.editorsyncValue();
43613     },
43614    
43615     
43616     createFontOptions : function(){
43617         var buf = [], fs = this.fontFamilies, ff, lc;
43618         
43619         
43620         
43621         for(var i = 0, len = fs.length; i< len; i++){
43622             ff = fs[i];
43623             lc = ff.toLowerCase();
43624             buf.push(
43625                 '<option value="',lc,'" style="font-family:',ff,';"',
43626                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43627                     ff,
43628                 '</option>'
43629             );
43630         }
43631         return buf.join('');
43632     },
43633     
43634     toggleSourceEdit : function(sourceEditMode){
43635         
43636         Roo.log("toolbar toogle");
43637         if(sourceEditMode === undefined){
43638             sourceEditMode = !this.sourceEditMode;
43639         }
43640         this.sourceEditMode = sourceEditMode === true;
43641         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43642         // just toggle the button?
43643         if(btn.pressed !== this.sourceEditMode){
43644             btn.toggle(this.sourceEditMode);
43645             return;
43646         }
43647         
43648         if(sourceEditMode){
43649             Roo.log("disabling buttons");
43650             this.tb.items.each(function(item){
43651                 if(item.cmd != 'sourceedit'){
43652                     item.disable();
43653                 }
43654             });
43655           
43656         }else{
43657             Roo.log("enabling buttons");
43658             if(this.editorcore.initialized){
43659                 this.tb.items.each(function(item){
43660                     item.enable();
43661                 });
43662             }
43663             
43664         }
43665         Roo.log("calling toggole on editor");
43666         // tell the editor that it's been pressed..
43667         this.editor.toggleSourceEdit(sourceEditMode);
43668        
43669     },
43670      /**
43671      * Object collection of toolbar tooltips for the buttons in the editor. The key
43672      * is the command id associated with that button and the value is a valid QuickTips object.
43673      * For example:
43674 <pre><code>
43675 {
43676     bold : {
43677         title: 'Bold (Ctrl+B)',
43678         text: 'Make the selected text bold.',
43679         cls: 'x-html-editor-tip'
43680     },
43681     italic : {
43682         title: 'Italic (Ctrl+I)',
43683         text: 'Make the selected text italic.',
43684         cls: 'x-html-editor-tip'
43685     },
43686     ...
43687 </code></pre>
43688     * @type Object
43689      */
43690     buttonTips : {
43691         bold : {
43692             title: 'Bold (Ctrl+B)',
43693             text: 'Make the selected text bold.',
43694             cls: 'x-html-editor-tip'
43695         },
43696         italic : {
43697             title: 'Italic (Ctrl+I)',
43698             text: 'Make the selected text italic.',
43699             cls: 'x-html-editor-tip'
43700         },
43701         underline : {
43702             title: 'Underline (Ctrl+U)',
43703             text: 'Underline the selected text.',
43704             cls: 'x-html-editor-tip'
43705         },
43706         increasefontsize : {
43707             title: 'Grow Text',
43708             text: 'Increase the font size.',
43709             cls: 'x-html-editor-tip'
43710         },
43711         decreasefontsize : {
43712             title: 'Shrink Text',
43713             text: 'Decrease the font size.',
43714             cls: 'x-html-editor-tip'
43715         },
43716         backcolor : {
43717             title: 'Text Highlight Color',
43718             text: 'Change the background color of the selected text.',
43719             cls: 'x-html-editor-tip'
43720         },
43721         forecolor : {
43722             title: 'Font Color',
43723             text: 'Change the color of the selected text.',
43724             cls: 'x-html-editor-tip'
43725         },
43726         justifyleft : {
43727             title: 'Align Text Left',
43728             text: 'Align text to the left.',
43729             cls: 'x-html-editor-tip'
43730         },
43731         justifycenter : {
43732             title: 'Center Text',
43733             text: 'Center text in the editor.',
43734             cls: 'x-html-editor-tip'
43735         },
43736         justifyright : {
43737             title: 'Align Text Right',
43738             text: 'Align text to the right.',
43739             cls: 'x-html-editor-tip'
43740         },
43741         insertunorderedlist : {
43742             title: 'Bullet List',
43743             text: 'Start a bulleted list.',
43744             cls: 'x-html-editor-tip'
43745         },
43746         insertorderedlist : {
43747             title: 'Numbered List',
43748             text: 'Start a numbered list.',
43749             cls: 'x-html-editor-tip'
43750         },
43751         createlink : {
43752             title: 'Hyperlink',
43753             text: 'Make the selected text a hyperlink.',
43754             cls: 'x-html-editor-tip'
43755         },
43756         sourceedit : {
43757             title: 'Source Edit',
43758             text: 'Switch to source editing mode.',
43759             cls: 'x-html-editor-tip'
43760         }
43761     },
43762     // private
43763     onDestroy : function(){
43764         if(this.rendered){
43765             
43766             this.tb.items.each(function(item){
43767                 if(item.menu){
43768                     item.menu.removeAll();
43769                     if(item.menu.el){
43770                         item.menu.el.destroy();
43771                     }
43772                 }
43773                 item.destroy();
43774             });
43775              
43776         }
43777     },
43778     onFirstFocus: function() {
43779         this.tb.items.each(function(item){
43780            item.enable();
43781         });
43782     }
43783 });
43784
43785
43786
43787
43788 // <script type="text/javascript">
43789 /*
43790  * Based on
43791  * Ext JS Library 1.1.1
43792  * Copyright(c) 2006-2007, Ext JS, LLC.
43793  *  
43794  
43795  */
43796
43797  
43798 /**
43799  * @class Roo.form.HtmlEditor.ToolbarContext
43800  * Context Toolbar
43801  * 
43802  * Usage:
43803  *
43804  new Roo.form.HtmlEditor({
43805     ....
43806     toolbars : [
43807         { xtype: 'ToolbarStandard', styles : {} }
43808         { xtype: 'ToolbarContext', disable : {} }
43809     ]
43810 })
43811
43812      
43813  * 
43814  * @config : {Object} disable List of elements to disable.. (not done yet.)
43815  * @config : {Object} styles  Map of styles available.
43816  * 
43817  */
43818
43819 Roo.form.HtmlEditor.ToolbarContext = function(config)
43820 {
43821     
43822     Roo.apply(this, config);
43823     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43824     // dont call parent... till later.
43825     this.styles = this.styles || {};
43826 }
43827
43828  
43829
43830 Roo.form.HtmlEditor.ToolbarContext.types = {
43831     'IMG' : {
43832         width : {
43833             title: "Width",
43834             width: 40
43835         },
43836         height:  {
43837             title: "Height",
43838             width: 40
43839         },
43840         align: {
43841             title: "Align",
43842             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43843             width : 80
43844             
43845         },
43846         border: {
43847             title: "Border",
43848             width: 40
43849         },
43850         alt: {
43851             title: "Alt",
43852             width: 120
43853         },
43854         src : {
43855             title: "Src",
43856             width: 220
43857         }
43858         
43859     },
43860     'A' : {
43861         name : {
43862             title: "Name",
43863             width: 50
43864         },
43865         target:  {
43866             title: "Target",
43867             width: 120
43868         },
43869         href:  {
43870             title: "Href",
43871             width: 220
43872         } // border?
43873         
43874     },
43875     'TABLE' : {
43876         rows : {
43877             title: "Rows",
43878             width: 20
43879         },
43880         cols : {
43881             title: "Cols",
43882             width: 20
43883         },
43884         width : {
43885             title: "Width",
43886             width: 40
43887         },
43888         height : {
43889             title: "Height",
43890             width: 40
43891         },
43892         border : {
43893             title: "Border",
43894             width: 20
43895         }
43896     },
43897     'TD' : {
43898         width : {
43899             title: "Width",
43900             width: 40
43901         },
43902         height : {
43903             title: "Height",
43904             width: 40
43905         },   
43906         align: {
43907             title: "Align",
43908             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43909             width: 80
43910         },
43911         valign: {
43912             title: "Valign",
43913             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43914             width: 80
43915         },
43916         colspan: {
43917             title: "Colspan",
43918             width: 20
43919             
43920         },
43921          'font-family'  : {
43922             title : "Font",
43923             style : 'fontFamily',
43924             displayField: 'display',
43925             optname : 'font-family',
43926             width: 140
43927         }
43928     },
43929     'INPUT' : {
43930         name : {
43931             title: "name",
43932             width: 120
43933         },
43934         value : {
43935             title: "Value",
43936             width: 120
43937         },
43938         width : {
43939             title: "Width",
43940             width: 40
43941         }
43942     },
43943     'LABEL' : {
43944         'for' : {
43945             title: "For",
43946             width: 120
43947         }
43948     },
43949     'TEXTAREA' : {
43950           name : {
43951             title: "name",
43952             width: 120
43953         },
43954         rows : {
43955             title: "Rows",
43956             width: 20
43957         },
43958         cols : {
43959             title: "Cols",
43960             width: 20
43961         }
43962     },
43963     'SELECT' : {
43964         name : {
43965             title: "name",
43966             width: 120
43967         },
43968         selectoptions : {
43969             title: "Options",
43970             width: 200
43971         }
43972     },
43973     
43974     // should we really allow this??
43975     // should this just be 
43976     'BODY' : {
43977         title : {
43978             title: "Title",
43979             width: 200,
43980             disabled : true
43981         }
43982     },
43983     'SPAN' : {
43984         'font-family'  : {
43985             title : "Font",
43986             style : 'fontFamily',
43987             displayField: 'display',
43988             optname : 'font-family',
43989             width: 140
43990         }
43991     },
43992     'DIV' : {
43993         'font-family'  : {
43994             title : "Font",
43995             style : 'fontFamily',
43996             displayField: 'display',
43997             optname : 'font-family',
43998             width: 140
43999         }
44000     },
44001      'P' : {
44002         'font-family'  : {
44003             title : "Font",
44004             style : 'fontFamily',
44005             displayField: 'display',
44006             optname : 'font-family',
44007             width: 140
44008         }
44009     },
44010     
44011     '*' : {
44012         // empty..
44013     }
44014
44015 };
44016
44017 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44018 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44019
44020 Roo.form.HtmlEditor.ToolbarContext.options = {
44021         'font-family'  : [ 
44022                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44023                 [ 'Courier New', 'Courier New'],
44024                 [ 'Tahoma', 'Tahoma'],
44025                 [ 'Times New Roman,serif', 'Times'],
44026                 [ 'Verdana','Verdana' ]
44027         ]
44028 };
44029
44030 // fixme - these need to be configurable..
44031  
44032
44033 Roo.form.HtmlEditor.ToolbarContext.types
44034
44035
44036 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44037     
44038     tb: false,
44039     
44040     rendered: false,
44041     
44042     editor : false,
44043     editorcore : false,
44044     /**
44045      * @cfg {Object} disable  List of toolbar elements to disable
44046          
44047      */
44048     disable : false,
44049     /**
44050      * @cfg {Object} styles List of styles 
44051      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44052      *
44053      * These must be defined in the page, so they get rendered correctly..
44054      * .headline { }
44055      * TD.underline { }
44056      * 
44057      */
44058     styles : false,
44059     
44060     options: false,
44061     
44062     toolbars : false,
44063     
44064     init : function(editor)
44065     {
44066         this.editor = editor;
44067         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44068         var editorcore = this.editorcore;
44069         
44070         var fid = editorcore.frameId;
44071         var etb = this;
44072         function btn(id, toggle, handler){
44073             var xid = fid + '-'+ id ;
44074             return {
44075                 id : xid,
44076                 cmd : id,
44077                 cls : 'x-btn-icon x-edit-'+id,
44078                 enableToggle:toggle !== false,
44079                 scope: editorcore, // was editor...
44080                 handler:handler||editorcore.relayBtnCmd,
44081                 clickEvent:'mousedown',
44082                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44083                 tabIndex:-1
44084             };
44085         }
44086         // create a new element.
44087         var wdiv = editor.wrap.createChild({
44088                 tag: 'div'
44089             }, editor.wrap.dom.firstChild.nextSibling, true);
44090         
44091         // can we do this more than once??
44092         
44093          // stop form submits
44094       
44095  
44096         // disable everything...
44097         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44098         this.toolbars = {};
44099            
44100         for (var i in  ty) {
44101           
44102             this.toolbars[i] = this.buildToolbar(ty[i],i);
44103         }
44104         this.tb = this.toolbars.BODY;
44105         this.tb.el.show();
44106         this.buildFooter();
44107         this.footer.show();
44108         editor.on('hide', function( ) { this.footer.hide() }, this);
44109         editor.on('show', function( ) { this.footer.show() }, this);
44110         
44111          
44112         this.rendered = true;
44113         
44114         // the all the btns;
44115         editor.on('editorevent', this.updateToolbar, this);
44116         // other toolbars need to implement this..
44117         //editor.on('editmodechange', this.updateToolbar, this);
44118     },
44119     
44120     
44121     
44122     /**
44123      * Protected method that will not generally be called directly. It triggers
44124      * a toolbar update by reading the markup state of the current selection in the editor.
44125      */
44126     updateToolbar: function(editor,ev,sel){
44127
44128         //Roo.log(ev);
44129         // capture mouse up - this is handy for selecting images..
44130         // perhaps should go somewhere else...
44131         if(!this.editorcore.activated){
44132              this.editor.onFirstFocus();
44133             return;
44134         }
44135         
44136         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44137         // selectNode - might want to handle IE?
44138         if (ev &&
44139             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44140             ev.target && ev.target.tagName == 'IMG') {
44141             // they have click on an image...
44142             // let's see if we can change the selection...
44143             sel = ev.target;
44144          
44145               var nodeRange = sel.ownerDocument.createRange();
44146             try {
44147                 nodeRange.selectNode(sel);
44148             } catch (e) {
44149                 nodeRange.selectNodeContents(sel);
44150             }
44151             //nodeRange.collapse(true);
44152             var s = this.editorcore.win.getSelection();
44153             s.removeAllRanges();
44154             s.addRange(nodeRange);
44155         }  
44156         
44157       
44158         var updateFooter = sel ? false : true;
44159         
44160         
44161         var ans = this.editorcore.getAllAncestors();
44162         
44163         // pick
44164         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44165         
44166         if (!sel) { 
44167             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44168             sel = sel ? sel : this.editorcore.doc.body;
44169             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44170             
44171         }
44172         // pick a menu that exists..
44173         var tn = sel.tagName.toUpperCase();
44174         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44175         
44176         tn = sel.tagName.toUpperCase();
44177         
44178         var lastSel = this.tb.selectedNode
44179         
44180         this.tb.selectedNode = sel;
44181         
44182         // if current menu does not match..
44183         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44184                 
44185             this.tb.el.hide();
44186             ///console.log("show: " + tn);
44187             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44188             this.tb.el.show();
44189             // update name
44190             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44191             
44192             
44193             // update attributes
44194             if (this.tb.fields) {
44195                 this.tb.fields.each(function(e) {
44196                     if (e.stylename) {
44197                         e.setValue(sel.style[e.stylename]);
44198                         return;
44199                     } 
44200                    e.setValue(sel.getAttribute(e.attrname));
44201                 });
44202             }
44203             
44204             var hasStyles = false;
44205             for(var i in this.styles) {
44206                 hasStyles = true;
44207                 break;
44208             }
44209             
44210             // update styles
44211             if (hasStyles) { 
44212                 var st = this.tb.fields.item(0);
44213                 
44214                 st.store.removeAll();
44215                
44216                 
44217                 var cn = sel.className.split(/\s+/);
44218                 
44219                 var avs = [];
44220                 if (this.styles['*']) {
44221                     
44222                     Roo.each(this.styles['*'], function(v) {
44223                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44224                     });
44225                 }
44226                 if (this.styles[tn]) { 
44227                     Roo.each(this.styles[tn], function(v) {
44228                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44229                     });
44230                 }
44231                 
44232                 st.store.loadData(avs);
44233                 st.collapse();
44234                 st.setValue(cn);
44235             }
44236             // flag our selected Node.
44237             this.tb.selectedNode = sel;
44238            
44239            
44240             Roo.menu.MenuMgr.hideAll();
44241
44242         }
44243         
44244         if (!updateFooter) {
44245             //this.footDisp.dom.innerHTML = ''; 
44246             return;
44247         }
44248         // update the footer
44249         //
44250         var html = '';
44251         
44252         this.footerEls = ans.reverse();
44253         Roo.each(this.footerEls, function(a,i) {
44254             if (!a) { return; }
44255             html += html.length ? ' &gt; '  :  '';
44256             
44257             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44258             
44259         });
44260        
44261         // 
44262         var sz = this.footDisp.up('td').getSize();
44263         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44264         this.footDisp.dom.style.marginLeft = '5px';
44265         
44266         this.footDisp.dom.style.overflow = 'hidden';
44267         
44268         this.footDisp.dom.innerHTML = html;
44269             
44270         //this.editorsyncValue();
44271     },
44272      
44273     
44274    
44275        
44276     // private
44277     onDestroy : function(){
44278         if(this.rendered){
44279             
44280             this.tb.items.each(function(item){
44281                 if(item.menu){
44282                     item.menu.removeAll();
44283                     if(item.menu.el){
44284                         item.menu.el.destroy();
44285                     }
44286                 }
44287                 item.destroy();
44288             });
44289              
44290         }
44291     },
44292     onFirstFocus: function() {
44293         // need to do this for all the toolbars..
44294         this.tb.items.each(function(item){
44295            item.enable();
44296         });
44297     },
44298     buildToolbar: function(tlist, nm)
44299     {
44300         var editor = this.editor;
44301         var editorcore = this.editorcore;
44302          // create a new element.
44303         var wdiv = editor.wrap.createChild({
44304                 tag: 'div'
44305             }, editor.wrap.dom.firstChild.nextSibling, true);
44306         
44307        
44308         var tb = new Roo.Toolbar(wdiv);
44309         // add the name..
44310         
44311         tb.add(nm+ ":&nbsp;");
44312         
44313         var styles = [];
44314         for(var i in this.styles) {
44315             styles.push(i);
44316         }
44317         
44318         // styles...
44319         if (styles && styles.length) {
44320             
44321             // this needs a multi-select checkbox...
44322             tb.addField( new Roo.form.ComboBox({
44323                 store: new Roo.data.SimpleStore({
44324                     id : 'val',
44325                     fields: ['val', 'selected'],
44326                     data : [] 
44327                 }),
44328                 name : '-roo-edit-className',
44329                 attrname : 'className',
44330                 displayField: 'val',
44331                 typeAhead: false,
44332                 mode: 'local',
44333                 editable : false,
44334                 triggerAction: 'all',
44335                 emptyText:'Select Style',
44336                 selectOnFocus:true,
44337                 width: 130,
44338                 listeners : {
44339                     'select': function(c, r, i) {
44340                         // initial support only for on class per el..
44341                         tb.selectedNode.className =  r ? r.get('val') : '';
44342                         editorcore.syncValue();
44343                     }
44344                 }
44345     
44346             }));
44347         }
44348         
44349         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44350         var tbops = tbc.options;
44351         
44352         for (var i in tlist) {
44353             
44354             var item = tlist[i];
44355             tb.add(item.title + ":&nbsp;");
44356             
44357             
44358             //optname == used so you can configure the options available..
44359             var opts = item.opts ? item.opts : false;
44360             if (item.optname) {
44361                 opts = tbops[item.optname];
44362            
44363             }
44364             
44365             if (opts) {
44366                 // opts == pulldown..
44367                 tb.addField( new Roo.form.ComboBox({
44368                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44369                         id : 'val',
44370                         fields: ['val', 'display'],
44371                         data : opts  
44372                     }),
44373                     name : '-roo-edit-' + i,
44374                     attrname : i,
44375                     stylename : item.style ? item.style : false,
44376                     displayField: item.displayField ? item.displayField : 'val',
44377                     valueField :  'val',
44378                     typeAhead: false,
44379                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44380                     editable : false,
44381                     triggerAction: 'all',
44382                     emptyText:'Select',
44383                     selectOnFocus:true,
44384                     width: item.width ? item.width  : 130,
44385                     listeners : {
44386                         'select': function(c, r, i) {
44387                             if (c.stylename) {
44388                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44389                                 return;
44390                             }
44391                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44392                         }
44393                     }
44394
44395                 }));
44396                 continue;
44397                     
44398                  
44399                 
44400                 tb.addField( new Roo.form.TextField({
44401                     name: i,
44402                     width: 100,
44403                     //allowBlank:false,
44404                     value: ''
44405                 }));
44406                 continue;
44407             }
44408             tb.addField( new Roo.form.TextField({
44409                 name: '-roo-edit-' + i,
44410                 attrname : i,
44411                 
44412                 width: item.width,
44413                 //allowBlank:true,
44414                 value: '',
44415                 listeners: {
44416                     'change' : function(f, nv, ov) {
44417                         tb.selectedNode.setAttribute(f.attrname, nv);
44418                     }
44419                 }
44420             }));
44421              
44422         }
44423         tb.addFill();
44424         var _this = this;
44425         tb.addButton( {
44426             text: 'Remove Tag',
44427     
44428             listeners : {
44429                 click : function ()
44430                 {
44431                     // remove
44432                     // undo does not work.
44433                      
44434                     var sn = tb.selectedNode;
44435                     
44436                     var pn = sn.parentNode;
44437                     
44438                     var stn =  sn.childNodes[0];
44439                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44440                     while (sn.childNodes.length) {
44441                         var node = sn.childNodes[0];
44442                         sn.removeChild(node);
44443                         //Roo.log(node);
44444                         pn.insertBefore(node, sn);
44445                         
44446                     }
44447                     pn.removeChild(sn);
44448                     var range = editorcore.createRange();
44449         
44450                     range.setStart(stn,0);
44451                     range.setEnd(en,0); //????
44452                     //range.selectNode(sel);
44453                     
44454                     
44455                     var selection = editorcore.getSelection();
44456                     selection.removeAllRanges();
44457                     selection.addRange(range);
44458                     
44459                     
44460                     
44461                     //_this.updateToolbar(null, null, pn);
44462                     _this.updateToolbar(null, null, null);
44463                     _this.footDisp.dom.innerHTML = ''; 
44464                 }
44465             }
44466             
44467                     
44468                 
44469             
44470         });
44471         
44472         
44473         tb.el.on('click', function(e){
44474             e.preventDefault(); // what does this do?
44475         });
44476         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44477         tb.el.hide();
44478         tb.name = nm;
44479         // dont need to disable them... as they will get hidden
44480         return tb;
44481          
44482         
44483     },
44484     buildFooter : function()
44485     {
44486         
44487         var fel = this.editor.wrap.createChild();
44488         this.footer = new Roo.Toolbar(fel);
44489         // toolbar has scrolly on left / right?
44490         var footDisp= new Roo.Toolbar.Fill();
44491         var _t = this;
44492         this.footer.add(
44493             {
44494                 text : '&lt;',
44495                 xtype: 'Button',
44496                 handler : function() {
44497                     _t.footDisp.scrollTo('left',0,true)
44498                 }
44499             }
44500         );
44501         this.footer.add( footDisp );
44502         this.footer.add( 
44503             {
44504                 text : '&gt;',
44505                 xtype: 'Button',
44506                 handler : function() {
44507                     // no animation..
44508                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44509                 }
44510             }
44511         );
44512         var fel = Roo.get(footDisp.el);
44513         fel.addClass('x-editor-context');
44514         this.footDispWrap = fel; 
44515         this.footDispWrap.overflow  = 'hidden';
44516         
44517         this.footDisp = fel.createChild();
44518         this.footDispWrap.on('click', this.onContextClick, this)
44519         
44520         
44521     },
44522     onContextClick : function (ev,dom)
44523     {
44524         ev.preventDefault();
44525         var  cn = dom.className;
44526         //Roo.log(cn);
44527         if (!cn.match(/x-ed-loc-/)) {
44528             return;
44529         }
44530         var n = cn.split('-').pop();
44531         var ans = this.footerEls;
44532         var sel = ans[n];
44533         
44534          // pick
44535         var range = this.editorcore.createRange();
44536         
44537         range.selectNodeContents(sel);
44538         //range.selectNode(sel);
44539         
44540         
44541         var selection = this.editorcore.getSelection();
44542         selection.removeAllRanges();
44543         selection.addRange(range);
44544         
44545         
44546         
44547         this.updateToolbar(null, null, sel);
44548         
44549         
44550     }
44551     
44552     
44553     
44554     
44555     
44556 });
44557
44558
44559
44560
44561
44562 /*
44563  * Based on:
44564  * Ext JS Library 1.1.1
44565  * Copyright(c) 2006-2007, Ext JS, LLC.
44566  *
44567  * Originally Released Under LGPL - original licence link has changed is not relivant.
44568  *
44569  * Fork - LGPL
44570  * <script type="text/javascript">
44571  */
44572  
44573 /**
44574  * @class Roo.form.BasicForm
44575  * @extends Roo.util.Observable
44576  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44577  * @constructor
44578  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44579  * @param {Object} config Configuration options
44580  */
44581 Roo.form.BasicForm = function(el, config){
44582     this.allItems = [];
44583     this.childForms = [];
44584     Roo.apply(this, config);
44585     /*
44586      * The Roo.form.Field items in this form.
44587      * @type MixedCollection
44588      */
44589      
44590      
44591     this.items = new Roo.util.MixedCollection(false, function(o){
44592         return o.id || (o.id = Roo.id());
44593     });
44594     this.addEvents({
44595         /**
44596          * @event beforeaction
44597          * Fires before any action is performed. Return false to cancel the action.
44598          * @param {Form} this
44599          * @param {Action} action The action to be performed
44600          */
44601         beforeaction: true,
44602         /**
44603          * @event actionfailed
44604          * Fires when an action fails.
44605          * @param {Form} this
44606          * @param {Action} action The action that failed
44607          */
44608         actionfailed : true,
44609         /**
44610          * @event actioncomplete
44611          * Fires when an action is completed.
44612          * @param {Form} this
44613          * @param {Action} action The action that completed
44614          */
44615         actioncomplete : true
44616     });
44617     if(el){
44618         this.initEl(el);
44619     }
44620     Roo.form.BasicForm.superclass.constructor.call(this);
44621 };
44622
44623 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44624     /**
44625      * @cfg {String} method
44626      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44627      */
44628     /**
44629      * @cfg {DataReader} reader
44630      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44631      * This is optional as there is built-in support for processing JSON.
44632      */
44633     /**
44634      * @cfg {DataReader} errorReader
44635      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44636      * This is completely optional as there is built-in support for processing JSON.
44637      */
44638     /**
44639      * @cfg {String} url
44640      * The URL to use for form actions if one isn't supplied in the action options.
44641      */
44642     /**
44643      * @cfg {Boolean} fileUpload
44644      * Set to true if this form is a file upload.
44645      */
44646      
44647     /**
44648      * @cfg {Object} baseParams
44649      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44650      */
44651      /**
44652      
44653     /**
44654      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44655      */
44656     timeout: 30,
44657
44658     // private
44659     activeAction : null,
44660
44661     /**
44662      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44663      * or setValues() data instead of when the form was first created.
44664      */
44665     trackResetOnLoad : false,
44666     
44667     
44668     /**
44669      * childForms - used for multi-tab forms
44670      * @type {Array}
44671      */
44672     childForms : false,
44673     
44674     /**
44675      * allItems - full list of fields.
44676      * @type {Array}
44677      */
44678     allItems : false,
44679     
44680     /**
44681      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44682      * element by passing it or its id or mask the form itself by passing in true.
44683      * @type Mixed
44684      */
44685     waitMsgTarget : false,
44686
44687     // private
44688     initEl : function(el){
44689         this.el = Roo.get(el);
44690         this.id = this.el.id || Roo.id();
44691         this.el.on('submit', this.onSubmit, this);
44692         this.el.addClass('x-form');
44693     },
44694
44695     // private
44696     onSubmit : function(e){
44697         e.stopEvent();
44698     },
44699
44700     /**
44701      * Returns true if client-side validation on the form is successful.
44702      * @return Boolean
44703      */
44704     isValid : function(){
44705         var valid = true;
44706         this.items.each(function(f){
44707            if(!f.validate()){
44708                valid = false;
44709            }
44710         });
44711         return valid;
44712     },
44713
44714     /**
44715      * Returns true if any fields in this form have changed since their original load.
44716      * @return Boolean
44717      */
44718     isDirty : function(){
44719         var dirty = false;
44720         this.items.each(function(f){
44721            if(f.isDirty()){
44722                dirty = true;
44723                return false;
44724            }
44725         });
44726         return dirty;
44727     },
44728
44729     /**
44730      * Performs a predefined action (submit or load) or custom actions you define on this form.
44731      * @param {String} actionName The name of the action type
44732      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44733      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44734      * accept other config options):
44735      * <pre>
44736 Property          Type             Description
44737 ----------------  ---------------  ----------------------------------------------------------------------------------
44738 url               String           The url for the action (defaults to the form's url)
44739 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44740 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44741 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44742                                    validate the form on the client (defaults to false)
44743      * </pre>
44744      * @return {BasicForm} this
44745      */
44746     doAction : function(action, options){
44747         if(typeof action == 'string'){
44748             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44749         }
44750         if(this.fireEvent('beforeaction', this, action) !== false){
44751             this.beforeAction(action);
44752             action.run.defer(100, action);
44753         }
44754         return this;
44755     },
44756
44757     /**
44758      * Shortcut to do a submit action.
44759      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44760      * @return {BasicForm} this
44761      */
44762     submit : function(options){
44763         this.doAction('submit', options);
44764         return this;
44765     },
44766
44767     /**
44768      * Shortcut to do a load action.
44769      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44770      * @return {BasicForm} this
44771      */
44772     load : function(options){
44773         this.doAction('load', options);
44774         return this;
44775     },
44776
44777     /**
44778      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44779      * @param {Record} record The record to edit
44780      * @return {BasicForm} this
44781      */
44782     updateRecord : function(record){
44783         record.beginEdit();
44784         var fs = record.fields;
44785         fs.each(function(f){
44786             var field = this.findField(f.name);
44787             if(field){
44788                 record.set(f.name, field.getValue());
44789             }
44790         }, this);
44791         record.endEdit();
44792         return this;
44793     },
44794
44795     /**
44796      * Loads an Roo.data.Record into this form.
44797      * @param {Record} record The record to load
44798      * @return {BasicForm} this
44799      */
44800     loadRecord : function(record){
44801         this.setValues(record.data);
44802         return this;
44803     },
44804
44805     // private
44806     beforeAction : function(action){
44807         var o = action.options;
44808         
44809        
44810         if(this.waitMsgTarget === true){
44811             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44812         }else if(this.waitMsgTarget){
44813             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44814             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44815         }else {
44816             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44817         }
44818          
44819     },
44820
44821     // private
44822     afterAction : function(action, success){
44823         this.activeAction = null;
44824         var o = action.options;
44825         
44826         if(this.waitMsgTarget === true){
44827             this.el.unmask();
44828         }else if(this.waitMsgTarget){
44829             this.waitMsgTarget.unmask();
44830         }else{
44831             Roo.MessageBox.updateProgress(1);
44832             Roo.MessageBox.hide();
44833         }
44834          
44835         if(success){
44836             if(o.reset){
44837                 this.reset();
44838             }
44839             Roo.callback(o.success, o.scope, [this, action]);
44840             this.fireEvent('actioncomplete', this, action);
44841             
44842         }else{
44843             
44844             // failure condition..
44845             // we have a scenario where updates need confirming.
44846             // eg. if a locking scenario exists..
44847             // we look for { errors : { needs_confirm : true }} in the response.
44848             if (
44849                 (typeof(action.result) != 'undefined')  &&
44850                 (typeof(action.result.errors) != 'undefined')  &&
44851                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44852            ){
44853                 var _t = this;
44854                 Roo.MessageBox.confirm(
44855                     "Change requires confirmation",
44856                     action.result.errorMsg,
44857                     function(r) {
44858                         if (r != 'yes') {
44859                             return;
44860                         }
44861                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44862                     }
44863                     
44864                 );
44865                 
44866                 
44867                 
44868                 return;
44869             }
44870             
44871             Roo.callback(o.failure, o.scope, [this, action]);
44872             // show an error message if no failed handler is set..
44873             if (!this.hasListener('actionfailed')) {
44874                 Roo.MessageBox.alert("Error",
44875                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44876                         action.result.errorMsg :
44877                         "Saving Failed, please check your entries or try again"
44878                 );
44879             }
44880             
44881             this.fireEvent('actionfailed', this, action);
44882         }
44883         
44884     },
44885
44886     /**
44887      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44888      * @param {String} id The value to search for
44889      * @return Field
44890      */
44891     findField : function(id){
44892         var field = this.items.get(id);
44893         if(!field){
44894             this.items.each(function(f){
44895                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44896                     field = f;
44897                     return false;
44898                 }
44899             });
44900         }
44901         return field || null;
44902     },
44903
44904     /**
44905      * Add a secondary form to this one, 
44906      * Used to provide tabbed forms. One form is primary, with hidden values 
44907      * which mirror the elements from the other forms.
44908      * 
44909      * @param {Roo.form.Form} form to add.
44910      * 
44911      */
44912     addForm : function(form)
44913     {
44914        
44915         if (this.childForms.indexOf(form) > -1) {
44916             // already added..
44917             return;
44918         }
44919         this.childForms.push(form);
44920         var n = '';
44921         Roo.each(form.allItems, function (fe) {
44922             
44923             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44924             if (this.findField(n)) { // already added..
44925                 return;
44926             }
44927             var add = new Roo.form.Hidden({
44928                 name : n
44929             });
44930             add.render(this.el);
44931             
44932             this.add( add );
44933         }, this);
44934         
44935     },
44936     /**
44937      * Mark fields in this form invalid in bulk.
44938      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44939      * @return {BasicForm} this
44940      */
44941     markInvalid : function(errors){
44942         if(errors instanceof Array){
44943             for(var i = 0, len = errors.length; i < len; i++){
44944                 var fieldError = errors[i];
44945                 var f = this.findField(fieldError.id);
44946                 if(f){
44947                     f.markInvalid(fieldError.msg);
44948                 }
44949             }
44950         }else{
44951             var field, id;
44952             for(id in errors){
44953                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44954                     field.markInvalid(errors[id]);
44955                 }
44956             }
44957         }
44958         Roo.each(this.childForms || [], function (f) {
44959             f.markInvalid(errors);
44960         });
44961         
44962         return this;
44963     },
44964
44965     /**
44966      * Set values for fields in this form in bulk.
44967      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44968      * @return {BasicForm} this
44969      */
44970     setValues : function(values){
44971         if(values instanceof Array){ // array of objects
44972             for(var i = 0, len = values.length; i < len; i++){
44973                 var v = values[i];
44974                 var f = this.findField(v.id);
44975                 if(f){
44976                     f.setValue(v.value);
44977                     if(this.trackResetOnLoad){
44978                         f.originalValue = f.getValue();
44979                     }
44980                 }
44981             }
44982         }else{ // object hash
44983             var field, id;
44984             for(id in values){
44985                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44986                     
44987                     if (field.setFromData && 
44988                         field.valueField && 
44989                         field.displayField &&
44990                         // combos' with local stores can 
44991                         // be queried via setValue()
44992                         // to set their value..
44993                         (field.store && !field.store.isLocal)
44994                         ) {
44995                         // it's a combo
44996                         var sd = { };
44997                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44998                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44999                         field.setFromData(sd);
45000                         
45001                     } else {
45002                         field.setValue(values[id]);
45003                     }
45004                     
45005                     
45006                     if(this.trackResetOnLoad){
45007                         field.originalValue = field.getValue();
45008                     }
45009                 }
45010             }
45011         }
45012          
45013         Roo.each(this.childForms || [], function (f) {
45014             f.setValues(values);
45015         });
45016                 
45017         return this;
45018     },
45019
45020     /**
45021      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45022      * they are returned as an array.
45023      * @param {Boolean} asString
45024      * @return {Object}
45025      */
45026     getValues : function(asString){
45027         if (this.childForms) {
45028             // copy values from the child forms
45029             Roo.each(this.childForms, function (f) {
45030                 this.setValues(f.getValues());
45031             }, this);
45032         }
45033         
45034         
45035         
45036         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45037         if(asString === true){
45038             return fs;
45039         }
45040         return Roo.urlDecode(fs);
45041     },
45042     
45043     /**
45044      * Returns the fields in this form as an object with key/value pairs. 
45045      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45046      * @return {Object}
45047      */
45048     getFieldValues : function(with_hidden)
45049     {
45050         if (this.childForms) {
45051             // copy values from the child forms
45052             // should this call getFieldValues - probably not as we do not currently copy
45053             // hidden fields when we generate..
45054             Roo.each(this.childForms, function (f) {
45055                 this.setValues(f.getValues());
45056             }, this);
45057         }
45058         
45059         var ret = {};
45060         this.items.each(function(f){
45061             if (!f.getName()) {
45062                 return;
45063             }
45064             var v = f.getValue();
45065             if (f.inputType =='radio') {
45066                 if (typeof(ret[f.getName()]) == 'undefined') {
45067                     ret[f.getName()] = ''; // empty..
45068                 }
45069                 
45070                 if (!f.el.dom.checked) {
45071                     return;
45072                     
45073                 }
45074                 v = f.el.dom.value;
45075                 
45076             }
45077             
45078             // not sure if this supported any more..
45079             if ((typeof(v) == 'object') && f.getRawValue) {
45080                 v = f.getRawValue() ; // dates..
45081             }
45082             // combo boxes where name != hiddenName...
45083             if (f.name != f.getName()) {
45084                 ret[f.name] = f.getRawValue();
45085             }
45086             ret[f.getName()] = v;
45087         });
45088         
45089         return ret;
45090     },
45091
45092     /**
45093      * Clears all invalid messages in this form.
45094      * @return {BasicForm} this
45095      */
45096     clearInvalid : function(){
45097         this.items.each(function(f){
45098            f.clearInvalid();
45099         });
45100         
45101         Roo.each(this.childForms || [], function (f) {
45102             f.clearInvalid();
45103         });
45104         
45105         
45106         return this;
45107     },
45108
45109     /**
45110      * Resets this form.
45111      * @return {BasicForm} this
45112      */
45113     reset : function(){
45114         this.items.each(function(f){
45115             f.reset();
45116         });
45117         
45118         Roo.each(this.childForms || [], function (f) {
45119             f.reset();
45120         });
45121        
45122         
45123         return this;
45124     },
45125
45126     /**
45127      * Add Roo.form components to this form.
45128      * @param {Field} field1
45129      * @param {Field} field2 (optional)
45130      * @param {Field} etc (optional)
45131      * @return {BasicForm} this
45132      */
45133     add : function(){
45134         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45135         return this;
45136     },
45137
45138
45139     /**
45140      * Removes a field from the items collection (does NOT remove its markup).
45141      * @param {Field} field
45142      * @return {BasicForm} this
45143      */
45144     remove : function(field){
45145         this.items.remove(field);
45146         return this;
45147     },
45148
45149     /**
45150      * Looks at the fields in this form, checks them for an id attribute,
45151      * and calls applyTo on the existing dom element with that id.
45152      * @return {BasicForm} this
45153      */
45154     render : function(){
45155         this.items.each(function(f){
45156             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45157                 f.applyTo(f.id);
45158             }
45159         });
45160         return this;
45161     },
45162
45163     /**
45164      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45165      * @param {Object} values
45166      * @return {BasicForm} this
45167      */
45168     applyToFields : function(o){
45169         this.items.each(function(f){
45170            Roo.apply(f, o);
45171         });
45172         return this;
45173     },
45174
45175     /**
45176      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45177      * @param {Object} values
45178      * @return {BasicForm} this
45179      */
45180     applyIfToFields : function(o){
45181         this.items.each(function(f){
45182            Roo.applyIf(f, o);
45183         });
45184         return this;
45185     }
45186 });
45187
45188 // back compat
45189 Roo.BasicForm = Roo.form.BasicForm;/*
45190  * Based on:
45191  * Ext JS Library 1.1.1
45192  * Copyright(c) 2006-2007, Ext JS, LLC.
45193  *
45194  * Originally Released Under LGPL - original licence link has changed is not relivant.
45195  *
45196  * Fork - LGPL
45197  * <script type="text/javascript">
45198  */
45199
45200 /**
45201  * @class Roo.form.Form
45202  * @extends Roo.form.BasicForm
45203  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45204  * @constructor
45205  * @param {Object} config Configuration options
45206  */
45207 Roo.form.Form = function(config){
45208     var xitems =  [];
45209     if (config.items) {
45210         xitems = config.items;
45211         delete config.items;
45212     }
45213    
45214     
45215     Roo.form.Form.superclass.constructor.call(this, null, config);
45216     this.url = this.url || this.action;
45217     if(!this.root){
45218         this.root = new Roo.form.Layout(Roo.applyIf({
45219             id: Roo.id()
45220         }, config));
45221     }
45222     this.active = this.root;
45223     /**
45224      * Array of all the buttons that have been added to this form via {@link addButton}
45225      * @type Array
45226      */
45227     this.buttons = [];
45228     this.allItems = [];
45229     this.addEvents({
45230         /**
45231          * @event clientvalidation
45232          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45233          * @param {Form} this
45234          * @param {Boolean} valid true if the form has passed client-side validation
45235          */
45236         clientvalidation: true,
45237         /**
45238          * @event rendered
45239          * Fires when the form is rendered
45240          * @param {Roo.form.Form} form
45241          */
45242         rendered : true
45243     });
45244     
45245     if (this.progressUrl) {
45246             // push a hidden field onto the list of fields..
45247             this.addxtype( {
45248                     xns: Roo.form, 
45249                     xtype : 'Hidden', 
45250                     name : 'UPLOAD_IDENTIFIER' 
45251             });
45252         }
45253         
45254     
45255     Roo.each(xitems, this.addxtype, this);
45256     
45257     
45258     
45259 };
45260
45261 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45262     /**
45263      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45264      */
45265     /**
45266      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45267      */
45268     /**
45269      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45270      */
45271     buttonAlign:'center',
45272
45273     /**
45274      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45275      */
45276     minButtonWidth:75,
45277
45278     /**
45279      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45280      * This property cascades to child containers if not set.
45281      */
45282     labelAlign:'left',
45283
45284     /**
45285      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45286      * fires a looping event with that state. This is required to bind buttons to the valid
45287      * state using the config value formBind:true on the button.
45288      */
45289     monitorValid : false,
45290
45291     /**
45292      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45293      */
45294     monitorPoll : 200,
45295     
45296     /**
45297      * @cfg {String} progressUrl - Url to return progress data 
45298      */
45299     
45300     progressUrl : false,
45301   
45302     /**
45303      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45304      * fields are added and the column is closed. If no fields are passed the column remains open
45305      * until end() is called.
45306      * @param {Object} config The config to pass to the column
45307      * @param {Field} field1 (optional)
45308      * @param {Field} field2 (optional)
45309      * @param {Field} etc (optional)
45310      * @return Column The column container object
45311      */
45312     column : function(c){
45313         var col = new Roo.form.Column(c);
45314         this.start(col);
45315         if(arguments.length > 1){ // duplicate code required because of Opera
45316             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45317             this.end();
45318         }
45319         return col;
45320     },
45321
45322     /**
45323      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45324      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45325      * until end() is called.
45326      * @param {Object} config The config to pass to the fieldset
45327      * @param {Field} field1 (optional)
45328      * @param {Field} field2 (optional)
45329      * @param {Field} etc (optional)
45330      * @return FieldSet The fieldset container object
45331      */
45332     fieldset : function(c){
45333         var fs = new Roo.form.FieldSet(c);
45334         this.start(fs);
45335         if(arguments.length > 1){ // duplicate code required because of Opera
45336             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45337             this.end();
45338         }
45339         return fs;
45340     },
45341
45342     /**
45343      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45344      * fields are added and the container is closed. If no fields are passed the container remains open
45345      * until end() is called.
45346      * @param {Object} config The config to pass to the Layout
45347      * @param {Field} field1 (optional)
45348      * @param {Field} field2 (optional)
45349      * @param {Field} etc (optional)
45350      * @return Layout The container object
45351      */
45352     container : function(c){
45353         var l = new Roo.form.Layout(c);
45354         this.start(l);
45355         if(arguments.length > 1){ // duplicate code required because of Opera
45356             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45357             this.end();
45358         }
45359         return l;
45360     },
45361
45362     /**
45363      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45364      * @param {Object} container A Roo.form.Layout or subclass of Layout
45365      * @return {Form} this
45366      */
45367     start : function(c){
45368         // cascade label info
45369         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45370         this.active.stack.push(c);
45371         c.ownerCt = this.active;
45372         this.active = c;
45373         return this;
45374     },
45375
45376     /**
45377      * Closes the current open container
45378      * @return {Form} this
45379      */
45380     end : function(){
45381         if(this.active == this.root){
45382             return this;
45383         }
45384         this.active = this.active.ownerCt;
45385         return this;
45386     },
45387
45388     /**
45389      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45390      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45391      * as the label of the field.
45392      * @param {Field} field1
45393      * @param {Field} field2 (optional)
45394      * @param {Field} etc. (optional)
45395      * @return {Form} this
45396      */
45397     add : function(){
45398         this.active.stack.push.apply(this.active.stack, arguments);
45399         this.allItems.push.apply(this.allItems,arguments);
45400         var r = [];
45401         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45402             if(a[i].isFormField){
45403                 r.push(a[i]);
45404             }
45405         }
45406         if(r.length > 0){
45407             Roo.form.Form.superclass.add.apply(this, r);
45408         }
45409         return this;
45410     },
45411     
45412
45413     
45414     
45415     
45416      /**
45417      * Find any element that has been added to a form, using it's ID or name
45418      * This can include framesets, columns etc. along with regular fields..
45419      * @param {String} id - id or name to find.
45420      
45421      * @return {Element} e - or false if nothing found.
45422      */
45423     findbyId : function(id)
45424     {
45425         var ret = false;
45426         if (!id) {
45427             return ret;
45428         }
45429         Roo.each(this.allItems, function(f){
45430             if (f.id == id || f.name == id ){
45431                 ret = f;
45432                 return false;
45433             }
45434         });
45435         return ret;
45436     },
45437
45438     
45439     
45440     /**
45441      * Render this form into the passed container. This should only be called once!
45442      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45443      * @return {Form} this
45444      */
45445     render : function(ct)
45446     {
45447         
45448         
45449         
45450         ct = Roo.get(ct);
45451         var o = this.autoCreate || {
45452             tag: 'form',
45453             method : this.method || 'POST',
45454             id : this.id || Roo.id()
45455         };
45456         this.initEl(ct.createChild(o));
45457
45458         this.root.render(this.el);
45459         
45460        
45461              
45462         this.items.each(function(f){
45463             f.render('x-form-el-'+f.id);
45464         });
45465
45466         if(this.buttons.length > 0){
45467             // tables are required to maintain order and for correct IE layout
45468             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45469                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45470                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45471             }}, null, true);
45472             var tr = tb.getElementsByTagName('tr')[0];
45473             for(var i = 0, len = this.buttons.length; i < len; i++) {
45474                 var b = this.buttons[i];
45475                 var td = document.createElement('td');
45476                 td.className = 'x-form-btn-td';
45477                 b.render(tr.appendChild(td));
45478             }
45479         }
45480         if(this.monitorValid){ // initialize after render
45481             this.startMonitoring();
45482         }
45483         this.fireEvent('rendered', this);
45484         return this;
45485     },
45486
45487     /**
45488      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45489      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45490      * object or a valid Roo.DomHelper element config
45491      * @param {Function} handler The function called when the button is clicked
45492      * @param {Object} scope (optional) The scope of the handler function
45493      * @return {Roo.Button}
45494      */
45495     addButton : function(config, handler, scope){
45496         var bc = {
45497             handler: handler,
45498             scope: scope,
45499             minWidth: this.minButtonWidth,
45500             hideParent:true
45501         };
45502         if(typeof config == "string"){
45503             bc.text = config;
45504         }else{
45505             Roo.apply(bc, config);
45506         }
45507         var btn = new Roo.Button(null, bc);
45508         this.buttons.push(btn);
45509         return btn;
45510     },
45511
45512      /**
45513      * Adds a series of form elements (using the xtype property as the factory method.
45514      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45515      * @param {Object} config 
45516      */
45517     
45518     addxtype : function()
45519     {
45520         var ar = Array.prototype.slice.call(arguments, 0);
45521         var ret = false;
45522         for(var i = 0; i < ar.length; i++) {
45523             if (!ar[i]) {
45524                 continue; // skip -- if this happends something invalid got sent, we 
45525                 // should ignore it, as basically that interface element will not show up
45526                 // and that should be pretty obvious!!
45527             }
45528             
45529             if (Roo.form[ar[i].xtype]) {
45530                 ar[i].form = this;
45531                 var fe = Roo.factory(ar[i], Roo.form);
45532                 if (!ret) {
45533                     ret = fe;
45534                 }
45535                 fe.form = this;
45536                 if (fe.store) {
45537                     fe.store.form = this;
45538                 }
45539                 if (fe.isLayout) {  
45540                          
45541                     this.start(fe);
45542                     this.allItems.push(fe);
45543                     if (fe.items && fe.addxtype) {
45544                         fe.addxtype.apply(fe, fe.items);
45545                         delete fe.items;
45546                     }
45547                      this.end();
45548                     continue;
45549                 }
45550                 
45551                 
45552                  
45553                 this.add(fe);
45554               //  console.log('adding ' + ar[i].xtype);
45555             }
45556             if (ar[i].xtype == 'Button') {  
45557                 //console.log('adding button');
45558                 //console.log(ar[i]);
45559                 this.addButton(ar[i]);
45560                 this.allItems.push(fe);
45561                 continue;
45562             }
45563             
45564             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45565                 alert('end is not supported on xtype any more, use items');
45566             //    this.end();
45567             //    //console.log('adding end');
45568             }
45569             
45570         }
45571         return ret;
45572     },
45573     
45574     /**
45575      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45576      * option "monitorValid"
45577      */
45578     startMonitoring : function(){
45579         if(!this.bound){
45580             this.bound = true;
45581             Roo.TaskMgr.start({
45582                 run : this.bindHandler,
45583                 interval : this.monitorPoll || 200,
45584                 scope: this
45585             });
45586         }
45587     },
45588
45589     /**
45590      * Stops monitoring of the valid state of this form
45591      */
45592     stopMonitoring : function(){
45593         this.bound = false;
45594     },
45595
45596     // private
45597     bindHandler : function(){
45598         if(!this.bound){
45599             return false; // stops binding
45600         }
45601         var valid = true;
45602         this.items.each(function(f){
45603             if(!f.isValid(true)){
45604                 valid = false;
45605                 return false;
45606             }
45607         });
45608         for(var i = 0, len = this.buttons.length; i < len; i++){
45609             var btn = this.buttons[i];
45610             if(btn.formBind === true && btn.disabled === valid){
45611                 btn.setDisabled(!valid);
45612             }
45613         }
45614         this.fireEvent('clientvalidation', this, valid);
45615     }
45616     
45617     
45618     
45619     
45620     
45621     
45622     
45623     
45624 });
45625
45626
45627 // back compat
45628 Roo.Form = Roo.form.Form;
45629 /*
45630  * Based on:
45631  * Ext JS Library 1.1.1
45632  * Copyright(c) 2006-2007, Ext JS, LLC.
45633  *
45634  * Originally Released Under LGPL - original licence link has changed is not relivant.
45635  *
45636  * Fork - LGPL
45637  * <script type="text/javascript">
45638  */
45639
45640 // as we use this in bootstrap.
45641 Roo.namespace('Roo.form');
45642  /**
45643  * @class Roo.form.Action
45644  * Internal Class used to handle form actions
45645  * @constructor
45646  * @param {Roo.form.BasicForm} el The form element or its id
45647  * @param {Object} config Configuration options
45648  */
45649
45650  
45651  
45652 // define the action interface
45653 Roo.form.Action = function(form, options){
45654     this.form = form;
45655     this.options = options || {};
45656 };
45657 /**
45658  * Client Validation Failed
45659  * @const 
45660  */
45661 Roo.form.Action.CLIENT_INVALID = 'client';
45662 /**
45663  * Server Validation Failed
45664  * @const 
45665  */
45666 Roo.form.Action.SERVER_INVALID = 'server';
45667  /**
45668  * Connect to Server Failed
45669  * @const 
45670  */
45671 Roo.form.Action.CONNECT_FAILURE = 'connect';
45672 /**
45673  * Reading Data from Server Failed
45674  * @const 
45675  */
45676 Roo.form.Action.LOAD_FAILURE = 'load';
45677
45678 Roo.form.Action.prototype = {
45679     type : 'default',
45680     failureType : undefined,
45681     response : undefined,
45682     result : undefined,
45683
45684     // interface method
45685     run : function(options){
45686
45687     },
45688
45689     // interface method
45690     success : function(response){
45691
45692     },
45693
45694     // interface method
45695     handleResponse : function(response){
45696
45697     },
45698
45699     // default connection failure
45700     failure : function(response){
45701         
45702         this.response = response;
45703         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45704         this.form.afterAction(this, false);
45705     },
45706
45707     processResponse : function(response){
45708         this.response = response;
45709         if(!response.responseText){
45710             return true;
45711         }
45712         this.result = this.handleResponse(response);
45713         return this.result;
45714     },
45715
45716     // utility functions used internally
45717     getUrl : function(appendParams){
45718         var url = this.options.url || this.form.url || this.form.el.dom.action;
45719         if(appendParams){
45720             var p = this.getParams();
45721             if(p){
45722                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45723             }
45724         }
45725         return url;
45726     },
45727
45728     getMethod : function(){
45729         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45730     },
45731
45732     getParams : function(){
45733         var bp = this.form.baseParams;
45734         var p = this.options.params;
45735         if(p){
45736             if(typeof p == "object"){
45737                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45738             }else if(typeof p == 'string' && bp){
45739                 p += '&' + Roo.urlEncode(bp);
45740             }
45741         }else if(bp){
45742             p = Roo.urlEncode(bp);
45743         }
45744         return p;
45745     },
45746
45747     createCallback : function(){
45748         return {
45749             success: this.success,
45750             failure: this.failure,
45751             scope: this,
45752             timeout: (this.form.timeout*1000),
45753             upload: this.form.fileUpload ? this.success : undefined
45754         };
45755     }
45756 };
45757
45758 Roo.form.Action.Submit = function(form, options){
45759     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45760 };
45761
45762 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45763     type : 'submit',
45764
45765     haveProgress : false,
45766     uploadComplete : false,
45767     
45768     // uploadProgress indicator.
45769     uploadProgress : function()
45770     {
45771         if (!this.form.progressUrl) {
45772             return;
45773         }
45774         
45775         if (!this.haveProgress) {
45776             Roo.MessageBox.progress("Uploading", "Uploading");
45777         }
45778         if (this.uploadComplete) {
45779            Roo.MessageBox.hide();
45780            return;
45781         }
45782         
45783         this.haveProgress = true;
45784    
45785         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45786         
45787         var c = new Roo.data.Connection();
45788         c.request({
45789             url : this.form.progressUrl,
45790             params: {
45791                 id : uid
45792             },
45793             method: 'GET',
45794             success : function(req){
45795                //console.log(data);
45796                 var rdata = false;
45797                 var edata;
45798                 try  {
45799                    rdata = Roo.decode(req.responseText)
45800                 } catch (e) {
45801                     Roo.log("Invalid data from server..");
45802                     Roo.log(edata);
45803                     return;
45804                 }
45805                 if (!rdata || !rdata.success) {
45806                     Roo.log(rdata);
45807                     Roo.MessageBox.alert(Roo.encode(rdata));
45808                     return;
45809                 }
45810                 var data = rdata.data;
45811                 
45812                 if (this.uploadComplete) {
45813                    Roo.MessageBox.hide();
45814                    return;
45815                 }
45816                    
45817                 if (data){
45818                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45819                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45820                     );
45821                 }
45822                 this.uploadProgress.defer(2000,this);
45823             },
45824        
45825             failure: function(data) {
45826                 Roo.log('progress url failed ');
45827                 Roo.log(data);
45828             },
45829             scope : this
45830         });
45831            
45832     },
45833     
45834     
45835     run : function()
45836     {
45837         // run get Values on the form, so it syncs any secondary forms.
45838         this.form.getValues();
45839         
45840         var o = this.options;
45841         var method = this.getMethod();
45842         var isPost = method == 'POST';
45843         if(o.clientValidation === false || this.form.isValid()){
45844             
45845             if (this.form.progressUrl) {
45846                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45847                     (new Date() * 1) + '' + Math.random());
45848                     
45849             } 
45850             
45851             
45852             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45853                 form:this.form.el.dom,
45854                 url:this.getUrl(!isPost),
45855                 method: method,
45856                 params:isPost ? this.getParams() : null,
45857                 isUpload: this.form.fileUpload
45858             }));
45859             
45860             this.uploadProgress();
45861
45862         }else if (o.clientValidation !== false){ // client validation failed
45863             this.failureType = Roo.form.Action.CLIENT_INVALID;
45864             this.form.afterAction(this, false);
45865         }
45866     },
45867
45868     success : function(response)
45869     {
45870         this.uploadComplete= true;
45871         if (this.haveProgress) {
45872             Roo.MessageBox.hide();
45873         }
45874         
45875         
45876         var result = this.processResponse(response);
45877         if(result === true || result.success){
45878             this.form.afterAction(this, true);
45879             return;
45880         }
45881         if(result.errors){
45882             this.form.markInvalid(result.errors);
45883             this.failureType = Roo.form.Action.SERVER_INVALID;
45884         }
45885         this.form.afterAction(this, false);
45886     },
45887     failure : function(response)
45888     {
45889         this.uploadComplete= true;
45890         if (this.haveProgress) {
45891             Roo.MessageBox.hide();
45892         }
45893         
45894         this.response = response;
45895         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45896         this.form.afterAction(this, false);
45897     },
45898     
45899     handleResponse : function(response){
45900         if(this.form.errorReader){
45901             var rs = this.form.errorReader.read(response);
45902             var errors = [];
45903             if(rs.records){
45904                 for(var i = 0, len = rs.records.length; i < len; i++) {
45905                     var r = rs.records[i];
45906                     errors[i] = r.data;
45907                 }
45908             }
45909             if(errors.length < 1){
45910                 errors = null;
45911             }
45912             return {
45913                 success : rs.success,
45914                 errors : errors
45915             };
45916         }
45917         var ret = false;
45918         try {
45919             ret = Roo.decode(response.responseText);
45920         } catch (e) {
45921             ret = {
45922                 success: false,
45923                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45924                 errors : []
45925             };
45926         }
45927         return ret;
45928         
45929     }
45930 });
45931
45932
45933 Roo.form.Action.Load = function(form, options){
45934     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45935     this.reader = this.form.reader;
45936 };
45937
45938 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45939     type : 'load',
45940
45941     run : function(){
45942         
45943         Roo.Ajax.request(Roo.apply(
45944                 this.createCallback(), {
45945                     method:this.getMethod(),
45946                     url:this.getUrl(false),
45947                     params:this.getParams()
45948         }));
45949     },
45950
45951     success : function(response){
45952         
45953         var result = this.processResponse(response);
45954         if(result === true || !result.success || !result.data){
45955             this.failureType = Roo.form.Action.LOAD_FAILURE;
45956             this.form.afterAction(this, false);
45957             return;
45958         }
45959         this.form.clearInvalid();
45960         this.form.setValues(result.data);
45961         this.form.afterAction(this, true);
45962     },
45963
45964     handleResponse : function(response){
45965         if(this.form.reader){
45966             var rs = this.form.reader.read(response);
45967             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45968             return {
45969                 success : rs.success,
45970                 data : data
45971             };
45972         }
45973         return Roo.decode(response.responseText);
45974     }
45975 });
45976
45977 Roo.form.Action.ACTION_TYPES = {
45978     'load' : Roo.form.Action.Load,
45979     'submit' : Roo.form.Action.Submit
45980 };/*
45981  * Based on:
45982  * Ext JS Library 1.1.1
45983  * Copyright(c) 2006-2007, Ext JS, LLC.
45984  *
45985  * Originally Released Under LGPL - original licence link has changed is not relivant.
45986  *
45987  * Fork - LGPL
45988  * <script type="text/javascript">
45989  */
45990  
45991 /**
45992  * @class Roo.form.Layout
45993  * @extends Roo.Component
45994  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45995  * @constructor
45996  * @param {Object} config Configuration options
45997  */
45998 Roo.form.Layout = function(config){
45999     var xitems = [];
46000     if (config.items) {
46001         xitems = config.items;
46002         delete config.items;
46003     }
46004     Roo.form.Layout.superclass.constructor.call(this, config);
46005     this.stack = [];
46006     Roo.each(xitems, this.addxtype, this);
46007      
46008 };
46009
46010 Roo.extend(Roo.form.Layout, Roo.Component, {
46011     /**
46012      * @cfg {String/Object} autoCreate
46013      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46014      */
46015     /**
46016      * @cfg {String/Object/Function} style
46017      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46018      * a function which returns such a specification.
46019      */
46020     /**
46021      * @cfg {String} labelAlign
46022      * Valid values are "left," "top" and "right" (defaults to "left")
46023      */
46024     /**
46025      * @cfg {Number} labelWidth
46026      * Fixed width in pixels of all field labels (defaults to undefined)
46027      */
46028     /**
46029      * @cfg {Boolean} clear
46030      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46031      */
46032     clear : true,
46033     /**
46034      * @cfg {String} labelSeparator
46035      * The separator to use after field labels (defaults to ':')
46036      */
46037     labelSeparator : ':',
46038     /**
46039      * @cfg {Boolean} hideLabels
46040      * True to suppress the display of field labels in this layout (defaults to false)
46041      */
46042     hideLabels : false,
46043
46044     // private
46045     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46046     
46047     isLayout : true,
46048     
46049     // private
46050     onRender : function(ct, position){
46051         if(this.el){ // from markup
46052             this.el = Roo.get(this.el);
46053         }else {  // generate
46054             var cfg = this.getAutoCreate();
46055             this.el = ct.createChild(cfg, position);
46056         }
46057         if(this.style){
46058             this.el.applyStyles(this.style);
46059         }
46060         if(this.labelAlign){
46061             this.el.addClass('x-form-label-'+this.labelAlign);
46062         }
46063         if(this.hideLabels){
46064             this.labelStyle = "display:none";
46065             this.elementStyle = "padding-left:0;";
46066         }else{
46067             if(typeof this.labelWidth == 'number'){
46068                 this.labelStyle = "width:"+this.labelWidth+"px;";
46069                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46070             }
46071             if(this.labelAlign == 'top'){
46072                 this.labelStyle = "width:auto;";
46073                 this.elementStyle = "padding-left:0;";
46074             }
46075         }
46076         var stack = this.stack;
46077         var slen = stack.length;
46078         if(slen > 0){
46079             if(!this.fieldTpl){
46080                 var t = new Roo.Template(
46081                     '<div class="x-form-item {5}">',
46082                         '<label for="{0}" style="{2}">{1}{4}</label>',
46083                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46084                         '</div>',
46085                     '</div><div class="x-form-clear-left"></div>'
46086                 );
46087                 t.disableFormats = true;
46088                 t.compile();
46089                 Roo.form.Layout.prototype.fieldTpl = t;
46090             }
46091             for(var i = 0; i < slen; i++) {
46092                 if(stack[i].isFormField){
46093                     this.renderField(stack[i]);
46094                 }else{
46095                     this.renderComponent(stack[i]);
46096                 }
46097             }
46098         }
46099         if(this.clear){
46100             this.el.createChild({cls:'x-form-clear'});
46101         }
46102     },
46103
46104     // private
46105     renderField : function(f){
46106         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46107                f.id, //0
46108                f.fieldLabel, //1
46109                f.labelStyle||this.labelStyle||'', //2
46110                this.elementStyle||'', //3
46111                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46112                f.itemCls||this.itemCls||''  //5
46113        ], true).getPrevSibling());
46114     },
46115
46116     // private
46117     renderComponent : function(c){
46118         c.render(c.isLayout ? this.el : this.el.createChild());    
46119     },
46120     /**
46121      * Adds a object form elements (using the xtype property as the factory method.)
46122      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46123      * @param {Object} config 
46124      */
46125     addxtype : function(o)
46126     {
46127         // create the lement.
46128         o.form = this.form;
46129         var fe = Roo.factory(o, Roo.form);
46130         this.form.allItems.push(fe);
46131         this.stack.push(fe);
46132         
46133         if (fe.isFormField) {
46134             this.form.items.add(fe);
46135         }
46136          
46137         return fe;
46138     }
46139 });
46140
46141 /**
46142  * @class Roo.form.Column
46143  * @extends Roo.form.Layout
46144  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46145  * @constructor
46146  * @param {Object} config Configuration options
46147  */
46148 Roo.form.Column = function(config){
46149     Roo.form.Column.superclass.constructor.call(this, config);
46150 };
46151
46152 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46153     /**
46154      * @cfg {Number/String} width
46155      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46156      */
46157     /**
46158      * @cfg {String/Object} autoCreate
46159      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46160      */
46161
46162     // private
46163     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46164
46165     // private
46166     onRender : function(ct, position){
46167         Roo.form.Column.superclass.onRender.call(this, ct, position);
46168         if(this.width){
46169             this.el.setWidth(this.width);
46170         }
46171     }
46172 });
46173
46174
46175 /**
46176  * @class Roo.form.Row
46177  * @extends Roo.form.Layout
46178  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46179  * @constructor
46180  * @param {Object} config Configuration options
46181  */
46182
46183  
46184 Roo.form.Row = function(config){
46185     Roo.form.Row.superclass.constructor.call(this, config);
46186 };
46187  
46188 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46189       /**
46190      * @cfg {Number/String} width
46191      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46192      */
46193     /**
46194      * @cfg {Number/String} height
46195      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46196      */
46197     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46198     
46199     padWidth : 20,
46200     // private
46201     onRender : function(ct, position){
46202         //console.log('row render');
46203         if(!this.rowTpl){
46204             var t = new Roo.Template(
46205                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46206                     '<label for="{0}" style="{2}">{1}{4}</label>',
46207                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46208                     '</div>',
46209                 '</div>'
46210             );
46211             t.disableFormats = true;
46212             t.compile();
46213             Roo.form.Layout.prototype.rowTpl = t;
46214         }
46215         this.fieldTpl = this.rowTpl;
46216         
46217         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46218         var labelWidth = 100;
46219         
46220         if ((this.labelAlign != 'top')) {
46221             if (typeof this.labelWidth == 'number') {
46222                 labelWidth = this.labelWidth
46223             }
46224             this.padWidth =  20 + labelWidth;
46225             
46226         }
46227         
46228         Roo.form.Column.superclass.onRender.call(this, ct, position);
46229         if(this.width){
46230             this.el.setWidth(this.width);
46231         }
46232         if(this.height){
46233             this.el.setHeight(this.height);
46234         }
46235     },
46236     
46237     // private
46238     renderField : function(f){
46239         f.fieldEl = this.fieldTpl.append(this.el, [
46240                f.id, f.fieldLabel,
46241                f.labelStyle||this.labelStyle||'',
46242                this.elementStyle||'',
46243                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46244                f.itemCls||this.itemCls||'',
46245                f.width ? f.width + this.padWidth : 160 + this.padWidth
46246        ],true);
46247     }
46248 });
46249  
46250
46251 /**
46252  * @class Roo.form.FieldSet
46253  * @extends Roo.form.Layout
46254  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46255  * @constructor
46256  * @param {Object} config Configuration options
46257  */
46258 Roo.form.FieldSet = function(config){
46259     Roo.form.FieldSet.superclass.constructor.call(this, config);
46260 };
46261
46262 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46263     /**
46264      * @cfg {String} legend
46265      * The text to display as the legend for the FieldSet (defaults to '')
46266      */
46267     /**
46268      * @cfg {String/Object} autoCreate
46269      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46270      */
46271
46272     // private
46273     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46274
46275     // private
46276     onRender : function(ct, position){
46277         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46278         if(this.legend){
46279             this.setLegend(this.legend);
46280         }
46281     },
46282
46283     // private
46284     setLegend : function(text){
46285         if(this.rendered){
46286             this.el.child('legend').update(text);
46287         }
46288     }
46289 });/*
46290  * Based on:
46291  * Ext JS Library 1.1.1
46292  * Copyright(c) 2006-2007, Ext JS, LLC.
46293  *
46294  * Originally Released Under LGPL - original licence link has changed is not relivant.
46295  *
46296  * Fork - LGPL
46297  * <script type="text/javascript">
46298  */
46299 /**
46300  * @class Roo.form.VTypes
46301  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46302  * @singleton
46303  */
46304 Roo.form.VTypes = function(){
46305     // closure these in so they are only created once.
46306     var alpha = /^[a-zA-Z_]+$/;
46307     var alphanum = /^[a-zA-Z0-9_]+$/;
46308     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46309     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46310
46311     // All these messages and functions are configurable
46312     return {
46313         /**
46314          * The function used to validate email addresses
46315          * @param {String} value The email address
46316          */
46317         'email' : function(v){
46318             return email.test(v);
46319         },
46320         /**
46321          * The error text to display when the email validation function returns false
46322          * @type String
46323          */
46324         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46325         /**
46326          * The keystroke filter mask to be applied on email input
46327          * @type RegExp
46328          */
46329         'emailMask' : /[a-z0-9_\.\-@]/i,
46330
46331         /**
46332          * The function used to validate URLs
46333          * @param {String} value The URL
46334          */
46335         'url' : function(v){
46336             return url.test(v);
46337         },
46338         /**
46339          * The error text to display when the url validation function returns false
46340          * @type String
46341          */
46342         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46343         
46344         /**
46345          * The function used to validate alpha values
46346          * @param {String} value The value
46347          */
46348         'alpha' : function(v){
46349             return alpha.test(v);
46350         },
46351         /**
46352          * The error text to display when the alpha validation function returns false
46353          * @type String
46354          */
46355         'alphaText' : 'This field should only contain letters and _',
46356         /**
46357          * The keystroke filter mask to be applied on alpha input
46358          * @type RegExp
46359          */
46360         'alphaMask' : /[a-z_]/i,
46361
46362         /**
46363          * The function used to validate alphanumeric values
46364          * @param {String} value The value
46365          */
46366         'alphanum' : function(v){
46367             return alphanum.test(v);
46368         },
46369         /**
46370          * The error text to display when the alphanumeric validation function returns false
46371          * @type String
46372          */
46373         'alphanumText' : 'This field should only contain letters, numbers and _',
46374         /**
46375          * The keystroke filter mask to be applied on alphanumeric input
46376          * @type RegExp
46377          */
46378         'alphanumMask' : /[a-z0-9_]/i
46379     };
46380 }();//<script type="text/javascript">
46381
46382 /**
46383  * @class Roo.form.FCKeditor
46384  * @extends Roo.form.TextArea
46385  * Wrapper around the FCKEditor http://www.fckeditor.net
46386  * @constructor
46387  * Creates a new FCKeditor
46388  * @param {Object} config Configuration options
46389  */
46390 Roo.form.FCKeditor = function(config){
46391     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46392     this.addEvents({
46393          /**
46394          * @event editorinit
46395          * Fired when the editor is initialized - you can add extra handlers here..
46396          * @param {FCKeditor} this
46397          * @param {Object} the FCK object.
46398          */
46399         editorinit : true
46400     });
46401     
46402     
46403 };
46404 Roo.form.FCKeditor.editors = { };
46405 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46406 {
46407     //defaultAutoCreate : {
46408     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46409     //},
46410     // private
46411     /**
46412      * @cfg {Object} fck options - see fck manual for details.
46413      */
46414     fckconfig : false,
46415     
46416     /**
46417      * @cfg {Object} fck toolbar set (Basic or Default)
46418      */
46419     toolbarSet : 'Basic',
46420     /**
46421      * @cfg {Object} fck BasePath
46422      */ 
46423     basePath : '/fckeditor/',
46424     
46425     
46426     frame : false,
46427     
46428     value : '',
46429     
46430    
46431     onRender : function(ct, position)
46432     {
46433         if(!this.el){
46434             this.defaultAutoCreate = {
46435                 tag: "textarea",
46436                 style:"width:300px;height:60px;",
46437                 autocomplete: "off"
46438             };
46439         }
46440         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46441         /*
46442         if(this.grow){
46443             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46444             if(this.preventScrollbars){
46445                 this.el.setStyle("overflow", "hidden");
46446             }
46447             this.el.setHeight(this.growMin);
46448         }
46449         */
46450         //console.log('onrender' + this.getId() );
46451         Roo.form.FCKeditor.editors[this.getId()] = this;
46452          
46453
46454         this.replaceTextarea() ;
46455         
46456     },
46457     
46458     getEditor : function() {
46459         return this.fckEditor;
46460     },
46461     /**
46462      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46463      * @param {Mixed} value The value to set
46464      */
46465     
46466     
46467     setValue : function(value)
46468     {
46469         //console.log('setValue: ' + value);
46470         
46471         if(typeof(value) == 'undefined') { // not sure why this is happending...
46472             return;
46473         }
46474         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46475         
46476         //if(!this.el || !this.getEditor()) {
46477         //    this.value = value;
46478             //this.setValue.defer(100,this,[value]);    
46479         //    return;
46480         //} 
46481         
46482         if(!this.getEditor()) {
46483             return;
46484         }
46485         
46486         this.getEditor().SetData(value);
46487         
46488         //
46489
46490     },
46491
46492     /**
46493      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46494      * @return {Mixed} value The field value
46495      */
46496     getValue : function()
46497     {
46498         
46499         if (this.frame && this.frame.dom.style.display == 'none') {
46500             return Roo.form.FCKeditor.superclass.getValue.call(this);
46501         }
46502         
46503         if(!this.el || !this.getEditor()) {
46504            
46505            // this.getValue.defer(100,this); 
46506             return this.value;
46507         }
46508        
46509         
46510         var value=this.getEditor().GetData();
46511         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46512         return Roo.form.FCKeditor.superclass.getValue.call(this);
46513         
46514
46515     },
46516
46517     /**
46518      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46519      * @return {Mixed} value The field value
46520      */
46521     getRawValue : function()
46522     {
46523         if (this.frame && this.frame.dom.style.display == 'none') {
46524             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46525         }
46526         
46527         if(!this.el || !this.getEditor()) {
46528             //this.getRawValue.defer(100,this); 
46529             return this.value;
46530             return;
46531         }
46532         
46533         
46534         
46535         var value=this.getEditor().GetData();
46536         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46537         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46538          
46539     },
46540     
46541     setSize : function(w,h) {
46542         
46543         
46544         
46545         //if (this.frame && this.frame.dom.style.display == 'none') {
46546         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46547         //    return;
46548         //}
46549         //if(!this.el || !this.getEditor()) {
46550         //    this.setSize.defer(100,this, [w,h]); 
46551         //    return;
46552         //}
46553         
46554         
46555         
46556         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46557         
46558         this.frame.dom.setAttribute('width', w);
46559         this.frame.dom.setAttribute('height', h);
46560         this.frame.setSize(w,h);
46561         
46562     },
46563     
46564     toggleSourceEdit : function(value) {
46565         
46566       
46567          
46568         this.el.dom.style.display = value ? '' : 'none';
46569         this.frame.dom.style.display = value ?  'none' : '';
46570         
46571     },
46572     
46573     
46574     focus: function(tag)
46575     {
46576         if (this.frame.dom.style.display == 'none') {
46577             return Roo.form.FCKeditor.superclass.focus.call(this);
46578         }
46579         if(!this.el || !this.getEditor()) {
46580             this.focus.defer(100,this, [tag]); 
46581             return;
46582         }
46583         
46584         
46585         
46586         
46587         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46588         this.getEditor().Focus();
46589         if (tgs.length) {
46590             if (!this.getEditor().Selection.GetSelection()) {
46591                 this.focus.defer(100,this, [tag]); 
46592                 return;
46593             }
46594             
46595             
46596             var r = this.getEditor().EditorDocument.createRange();
46597             r.setStart(tgs[0],0);
46598             r.setEnd(tgs[0],0);
46599             this.getEditor().Selection.GetSelection().removeAllRanges();
46600             this.getEditor().Selection.GetSelection().addRange(r);
46601             this.getEditor().Focus();
46602         }
46603         
46604     },
46605     
46606     
46607     
46608     replaceTextarea : function()
46609     {
46610         if ( document.getElementById( this.getId() + '___Frame' ) )
46611             return ;
46612         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46613         //{
46614             // We must check the elements firstly using the Id and then the name.
46615         var oTextarea = document.getElementById( this.getId() );
46616         
46617         var colElementsByName = document.getElementsByName( this.getId() ) ;
46618          
46619         oTextarea.style.display = 'none' ;
46620
46621         if ( oTextarea.tabIndex ) {            
46622             this.TabIndex = oTextarea.tabIndex ;
46623         }
46624         
46625         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46626         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46627         this.frame = Roo.get(this.getId() + '___Frame')
46628     },
46629     
46630     _getConfigHtml : function()
46631     {
46632         var sConfig = '' ;
46633
46634         for ( var o in this.fckconfig ) {
46635             sConfig += sConfig.length > 0  ? '&amp;' : '';
46636             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46637         }
46638
46639         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46640     },
46641     
46642     
46643     _getIFrameHtml : function()
46644     {
46645         var sFile = 'fckeditor.html' ;
46646         /* no idea what this is about..
46647         try
46648         {
46649             if ( (/fcksource=true/i).test( window.top.location.search ) )
46650                 sFile = 'fckeditor.original.html' ;
46651         }
46652         catch (e) { 
46653         */
46654
46655         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46656         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46657         
46658         
46659         var html = '<iframe id="' + this.getId() +
46660             '___Frame" src="' + sLink +
46661             '" width="' + this.width +
46662             '" height="' + this.height + '"' +
46663             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46664             ' frameborder="0" scrolling="no"></iframe>' ;
46665
46666         return html ;
46667     },
46668     
46669     _insertHtmlBefore : function( html, element )
46670     {
46671         if ( element.insertAdjacentHTML )       {
46672             // IE
46673             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46674         } else { // Gecko
46675             var oRange = document.createRange() ;
46676             oRange.setStartBefore( element ) ;
46677             var oFragment = oRange.createContextualFragment( html );
46678             element.parentNode.insertBefore( oFragment, element ) ;
46679         }
46680     }
46681     
46682     
46683   
46684     
46685     
46686     
46687     
46688
46689 });
46690
46691 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46692
46693 function FCKeditor_OnComplete(editorInstance){
46694     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46695     f.fckEditor = editorInstance;
46696     //console.log("loaded");
46697     f.fireEvent('editorinit', f, editorInstance);
46698
46699   
46700
46701  
46702
46703
46704
46705
46706
46707
46708
46709
46710
46711
46712
46713
46714
46715
46716
46717 //<script type="text/javascript">
46718 /**
46719  * @class Roo.form.GridField
46720  * @extends Roo.form.Field
46721  * Embed a grid (or editable grid into a form)
46722  * STATUS ALPHA
46723  * 
46724  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46725  * it needs 
46726  * xgrid.store = Roo.data.Store
46727  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46728  * xgrid.store.reader = Roo.data.JsonReader 
46729  * 
46730  * 
46731  * @constructor
46732  * Creates a new GridField
46733  * @param {Object} config Configuration options
46734  */
46735 Roo.form.GridField = function(config){
46736     Roo.form.GridField.superclass.constructor.call(this, config);
46737      
46738 };
46739
46740 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46741     /**
46742      * @cfg {Number} width  - used to restrict width of grid..
46743      */
46744     width : 100,
46745     /**
46746      * @cfg {Number} height - used to restrict height of grid..
46747      */
46748     height : 50,
46749      /**
46750      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46751          * 
46752          *}
46753      */
46754     xgrid : false, 
46755     /**
46756      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46757      * {tag: "input", type: "checkbox", autocomplete: "off"})
46758      */
46759    // defaultAutoCreate : { tag: 'div' },
46760     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46761     /**
46762      * @cfg {String} addTitle Text to include for adding a title.
46763      */
46764     addTitle : false,
46765     //
46766     onResize : function(){
46767         Roo.form.Field.superclass.onResize.apply(this, arguments);
46768     },
46769
46770     initEvents : function(){
46771         // Roo.form.Checkbox.superclass.initEvents.call(this);
46772         // has no events...
46773        
46774     },
46775
46776
46777     getResizeEl : function(){
46778         return this.wrap;
46779     },
46780
46781     getPositionEl : function(){
46782         return this.wrap;
46783     },
46784
46785     // private
46786     onRender : function(ct, position){
46787         
46788         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46789         var style = this.style;
46790         delete this.style;
46791         
46792         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46793         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46794         this.viewEl = this.wrap.createChild({ tag: 'div' });
46795         if (style) {
46796             this.viewEl.applyStyles(style);
46797         }
46798         if (this.width) {
46799             this.viewEl.setWidth(this.width);
46800         }
46801         if (this.height) {
46802             this.viewEl.setHeight(this.height);
46803         }
46804         //if(this.inputValue !== undefined){
46805         //this.setValue(this.value);
46806         
46807         
46808         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46809         
46810         
46811         this.grid.render();
46812         this.grid.getDataSource().on('remove', this.refreshValue, this);
46813         this.grid.getDataSource().on('update', this.refreshValue, this);
46814         this.grid.on('afteredit', this.refreshValue, this);
46815  
46816     },
46817      
46818     
46819     /**
46820      * Sets the value of the item. 
46821      * @param {String} either an object  or a string..
46822      */
46823     setValue : function(v){
46824         //this.value = v;
46825         v = v || []; // empty set..
46826         // this does not seem smart - it really only affects memoryproxy grids..
46827         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46828             var ds = this.grid.getDataSource();
46829             // assumes a json reader..
46830             var data = {}
46831             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46832             ds.loadData( data);
46833         }
46834         // clear selection so it does not get stale.
46835         if (this.grid.sm) { 
46836             this.grid.sm.clearSelections();
46837         }
46838         
46839         Roo.form.GridField.superclass.setValue.call(this, v);
46840         this.refreshValue();
46841         // should load data in the grid really....
46842     },
46843     
46844     // private
46845     refreshValue: function() {
46846          var val = [];
46847         this.grid.getDataSource().each(function(r) {
46848             val.push(r.data);
46849         });
46850         this.el.dom.value = Roo.encode(val);
46851     }
46852     
46853      
46854     
46855     
46856 });/*
46857  * Based on:
46858  * Ext JS Library 1.1.1
46859  * Copyright(c) 2006-2007, Ext JS, LLC.
46860  *
46861  * Originally Released Under LGPL - original licence link has changed is not relivant.
46862  *
46863  * Fork - LGPL
46864  * <script type="text/javascript">
46865  */
46866 /**
46867  * @class Roo.form.DisplayField
46868  * @extends Roo.form.Field
46869  * A generic Field to display non-editable data.
46870  * @constructor
46871  * Creates a new Display Field item.
46872  * @param {Object} config Configuration options
46873  */
46874 Roo.form.DisplayField = function(config){
46875     Roo.form.DisplayField.superclass.constructor.call(this, config);
46876     
46877 };
46878
46879 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46880     inputType:      'hidden',
46881     allowBlank:     true,
46882     readOnly:         true,
46883     
46884  
46885     /**
46886      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46887      */
46888     focusClass : undefined,
46889     /**
46890      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46891      */
46892     fieldClass: 'x-form-field',
46893     
46894      /**
46895      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46896      */
46897     valueRenderer: undefined,
46898     
46899     width: 100,
46900     /**
46901      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46902      * {tag: "input", type: "checkbox", autocomplete: "off"})
46903      */
46904      
46905  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46906
46907     onResize : function(){
46908         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46909         
46910     },
46911
46912     initEvents : function(){
46913         // Roo.form.Checkbox.superclass.initEvents.call(this);
46914         // has no events...
46915        
46916     },
46917
46918
46919     getResizeEl : function(){
46920         return this.wrap;
46921     },
46922
46923     getPositionEl : function(){
46924         return this.wrap;
46925     },
46926
46927     // private
46928     onRender : function(ct, position){
46929         
46930         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46931         //if(this.inputValue !== undefined){
46932         this.wrap = this.el.wrap();
46933         
46934         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46935         
46936         if (this.bodyStyle) {
46937             this.viewEl.applyStyles(this.bodyStyle);
46938         }
46939         //this.viewEl.setStyle('padding', '2px');
46940         
46941         this.setValue(this.value);
46942         
46943     },
46944 /*
46945     // private
46946     initValue : Roo.emptyFn,
46947
46948   */
46949
46950         // private
46951     onClick : function(){
46952         
46953     },
46954
46955     /**
46956      * Sets the checked state of the checkbox.
46957      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46958      */
46959     setValue : function(v){
46960         this.value = v;
46961         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46962         // this might be called before we have a dom element..
46963         if (!this.viewEl) {
46964             return;
46965         }
46966         this.viewEl.dom.innerHTML = html;
46967         Roo.form.DisplayField.superclass.setValue.call(this, v);
46968
46969     }
46970 });/*
46971  * 
46972  * Licence- LGPL
46973  * 
46974  */
46975
46976 /**
46977  * @class Roo.form.DayPicker
46978  * @extends Roo.form.Field
46979  * A Day picker show [M] [T] [W] ....
46980  * @constructor
46981  * Creates a new Day Picker
46982  * @param {Object} config Configuration options
46983  */
46984 Roo.form.DayPicker= function(config){
46985     Roo.form.DayPicker.superclass.constructor.call(this, config);
46986      
46987 };
46988
46989 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46990     /**
46991      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46992      */
46993     focusClass : undefined,
46994     /**
46995      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46996      */
46997     fieldClass: "x-form-field",
46998    
46999     /**
47000      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47001      * {tag: "input", type: "checkbox", autocomplete: "off"})
47002      */
47003     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
47004     
47005    
47006     actionMode : 'viewEl', 
47007     //
47008     // private
47009  
47010     inputType : 'hidden',
47011     
47012      
47013     inputElement: false, // real input element?
47014     basedOn: false, // ????
47015     
47016     isFormField: true, // not sure where this is needed!!!!
47017
47018     onResize : function(){
47019         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47020         if(!this.boxLabel){
47021             this.el.alignTo(this.wrap, 'c-c');
47022         }
47023     },
47024
47025     initEvents : function(){
47026         Roo.form.Checkbox.superclass.initEvents.call(this);
47027         this.el.on("click", this.onClick,  this);
47028         this.el.on("change", this.onClick,  this);
47029     },
47030
47031
47032     getResizeEl : function(){
47033         return this.wrap;
47034     },
47035
47036     getPositionEl : function(){
47037         return this.wrap;
47038     },
47039
47040     
47041     // private
47042     onRender : function(ct, position){
47043         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47044        
47045         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47046         
47047         var r1 = '<table><tr>';
47048         var r2 = '<tr class="x-form-daypick-icons">';
47049         for (var i=0; i < 7; i++) {
47050             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47051             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47052         }
47053         
47054         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47055         viewEl.select('img').on('click', this.onClick, this);
47056         this.viewEl = viewEl;   
47057         
47058         
47059         // this will not work on Chrome!!!
47060         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47061         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47062         
47063         
47064           
47065
47066     },
47067
47068     // private
47069     initValue : Roo.emptyFn,
47070
47071     /**
47072      * Returns the checked state of the checkbox.
47073      * @return {Boolean} True if checked, else false
47074      */
47075     getValue : function(){
47076         return this.el.dom.value;
47077         
47078     },
47079
47080         // private
47081     onClick : function(e){ 
47082         //this.setChecked(!this.checked);
47083         Roo.get(e.target).toggleClass('x-menu-item-checked');
47084         this.refreshValue();
47085         //if(this.el.dom.checked != this.checked){
47086         //    this.setValue(this.el.dom.checked);
47087        // }
47088     },
47089     
47090     // private
47091     refreshValue : function()
47092     {
47093         var val = '';
47094         this.viewEl.select('img',true).each(function(e,i,n)  {
47095             val += e.is(".x-menu-item-checked") ? String(n) : '';
47096         });
47097         this.setValue(val, true);
47098     },
47099
47100     /**
47101      * Sets the checked state of the checkbox.
47102      * On is always based on a string comparison between inputValue and the param.
47103      * @param {Boolean/String} value - the value to set 
47104      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47105      */
47106     setValue : function(v,suppressEvent){
47107         if (!this.el.dom) {
47108             return;
47109         }
47110         var old = this.el.dom.value ;
47111         this.el.dom.value = v;
47112         if (suppressEvent) {
47113             return ;
47114         }
47115          
47116         // update display..
47117         this.viewEl.select('img',true).each(function(e,i,n)  {
47118             
47119             var on = e.is(".x-menu-item-checked");
47120             var newv = v.indexOf(String(n)) > -1;
47121             if (on != newv) {
47122                 e.toggleClass('x-menu-item-checked');
47123             }
47124             
47125         });
47126         
47127         
47128         this.fireEvent('change', this, v, old);
47129         
47130         
47131     },
47132    
47133     // handle setting of hidden value by some other method!!?!?
47134     setFromHidden: function()
47135     {
47136         if(!this.el){
47137             return;
47138         }
47139         //console.log("SET FROM HIDDEN");
47140         //alert('setFrom hidden');
47141         this.setValue(this.el.dom.value);
47142     },
47143     
47144     onDestroy : function()
47145     {
47146         if(this.viewEl){
47147             Roo.get(this.viewEl).remove();
47148         }
47149          
47150         Roo.form.DayPicker.superclass.onDestroy.call(this);
47151     }
47152
47153 });/*
47154  * RooJS Library 1.1.1
47155  * Copyright(c) 2008-2011  Alan Knowles
47156  *
47157  * License - LGPL
47158  */
47159  
47160
47161 /**
47162  * @class Roo.form.ComboCheck
47163  * @extends Roo.form.ComboBox
47164  * A combobox for multiple select items.
47165  *
47166  * FIXME - could do with a reset button..
47167  * 
47168  * @constructor
47169  * Create a new ComboCheck
47170  * @param {Object} config Configuration options
47171  */
47172 Roo.form.ComboCheck = function(config){
47173     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47174     // should verify some data...
47175     // like
47176     // hiddenName = required..
47177     // displayField = required
47178     // valudField == required
47179     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47180     var _t = this;
47181     Roo.each(req, function(e) {
47182         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47183             throw "Roo.form.ComboCheck : missing value for: " + e;
47184         }
47185     });
47186     
47187     
47188 };
47189
47190 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47191      
47192      
47193     editable : false,
47194      
47195     selectedClass: 'x-menu-item-checked', 
47196     
47197     // private
47198     onRender : function(ct, position){
47199         var _t = this;
47200         
47201         
47202         
47203         if(!this.tpl){
47204             var cls = 'x-combo-list';
47205
47206             
47207             this.tpl =  new Roo.Template({
47208                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47209                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47210                    '<span>{' + this.displayField + '}</span>' +
47211                     '</div>' 
47212                 
47213             });
47214         }
47215  
47216         
47217         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47218         this.view.singleSelect = false;
47219         this.view.multiSelect = true;
47220         this.view.toggleSelect = true;
47221         this.pageTb.add(new Roo.Toolbar.Fill(), {
47222             
47223             text: 'Done',
47224             handler: function()
47225             {
47226                 _t.collapse();
47227             }
47228         });
47229     },
47230     
47231     onViewOver : function(e, t){
47232         // do nothing...
47233         return;
47234         
47235     },
47236     
47237     onViewClick : function(doFocus,index){
47238         return;
47239         
47240     },
47241     select: function () {
47242         //Roo.log("SELECT CALLED");
47243     },
47244      
47245     selectByValue : function(xv, scrollIntoView){
47246         var ar = this.getValueArray();
47247         var sels = [];
47248         
47249         Roo.each(ar, function(v) {
47250             if(v === undefined || v === null){
47251                 return;
47252             }
47253             var r = this.findRecord(this.valueField, v);
47254             if(r){
47255                 sels.push(this.store.indexOf(r))
47256                 
47257             }
47258         },this);
47259         this.view.select(sels);
47260         return false;
47261     },
47262     
47263     
47264     
47265     onSelect : function(record, index){
47266        // Roo.log("onselect Called");
47267        // this is only called by the clear button now..
47268         this.view.clearSelections();
47269         this.setValue('[]');
47270         if (this.value != this.valueBefore) {
47271             this.fireEvent('change', this, this.value, this.valueBefore);
47272             this.valueBefore = this.value;
47273         }
47274     },
47275     getValueArray : function()
47276     {
47277         var ar = [] ;
47278         
47279         try {
47280             //Roo.log(this.value);
47281             if (typeof(this.value) == 'undefined') {
47282                 return [];
47283             }
47284             var ar = Roo.decode(this.value);
47285             return  ar instanceof Array ? ar : []; //?? valid?
47286             
47287         } catch(e) {
47288             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47289             return [];
47290         }
47291          
47292     },
47293     expand : function ()
47294     {
47295         
47296         Roo.form.ComboCheck.superclass.expand.call(this);
47297         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47298         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47299         
47300
47301     },
47302     
47303     collapse : function(){
47304         Roo.form.ComboCheck.superclass.collapse.call(this);
47305         var sl = this.view.getSelectedIndexes();
47306         var st = this.store;
47307         var nv = [];
47308         var tv = [];
47309         var r;
47310         Roo.each(sl, function(i) {
47311             r = st.getAt(i);
47312             nv.push(r.get(this.valueField));
47313         },this);
47314         this.setValue(Roo.encode(nv));
47315         if (this.value != this.valueBefore) {
47316
47317             this.fireEvent('change', this, this.value, this.valueBefore);
47318             this.valueBefore = this.value;
47319         }
47320         
47321     },
47322     
47323     setValue : function(v){
47324         // Roo.log(v);
47325         this.value = v;
47326         
47327         var vals = this.getValueArray();
47328         var tv = [];
47329         Roo.each(vals, function(k) {
47330             var r = this.findRecord(this.valueField, k);
47331             if(r){
47332                 tv.push(r.data[this.displayField]);
47333             }else if(this.valueNotFoundText !== undefined){
47334                 tv.push( this.valueNotFoundText );
47335             }
47336         },this);
47337        // Roo.log(tv);
47338         
47339         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47340         this.hiddenField.value = v;
47341         this.value = v;
47342     }
47343     
47344 });/*
47345  * Based on:
47346  * Ext JS Library 1.1.1
47347  * Copyright(c) 2006-2007, Ext JS, LLC.
47348  *
47349  * Originally Released Under LGPL - original licence link has changed is not relivant.
47350  *
47351  * Fork - LGPL
47352  * <script type="text/javascript">
47353  */
47354  
47355 /**
47356  * @class Roo.form.Signature
47357  * @extends Roo.form.Field
47358  * Signature field.  
47359  * @constructor
47360  * 
47361  * @param {Object} config Configuration options
47362  */
47363
47364 Roo.form.Signature = function(config){
47365     Roo.form.Signature.superclass.constructor.call(this, config);
47366     
47367     this.addEvents({// not in used??
47368          /**
47369          * @event confirm
47370          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47371              * @param {Roo.form.Signature} combo This combo box
47372              */
47373         'confirm' : true,
47374         /**
47375          * @event reset
47376          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47377              * @param {Roo.form.ComboBox} combo This combo box
47378              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47379              */
47380         'reset' : true
47381     });
47382 };
47383
47384 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47385     /**
47386      * @cfg {Object} labels Label to use when rendering a form.
47387      * defaults to 
47388      * labels : { 
47389      *      clear : "Clear",
47390      *      confirm : "Confirm"
47391      *  }
47392      */
47393     labels : { 
47394         clear : "Clear",
47395         confirm : "Confirm"
47396     },
47397     /**
47398      * @cfg {Number} width The signature panel width (defaults to 300)
47399      */
47400     width: 300,
47401     /**
47402      * @cfg {Number} height The signature panel height (defaults to 100)
47403      */
47404     height : 100,
47405     /**
47406      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47407      */
47408     allowBlank : false,
47409     
47410     //private
47411     // {Object} signPanel The signature SVG panel element (defaults to {})
47412     signPanel : {},
47413     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47414     isMouseDown : false,
47415     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47416     isConfirmed : false,
47417     // {String} signatureTmp SVG mapping string (defaults to empty string)
47418     signatureTmp : '',
47419     
47420     
47421     defaultAutoCreate : { // modified by initCompnoent..
47422         tag: "input",
47423         type:"hidden"
47424     },
47425
47426     // private
47427     onRender : function(ct, position){
47428         
47429         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47430         
47431         this.wrap = this.el.wrap({
47432             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47433         });
47434         
47435         this.createToolbar(this);
47436         this.signPanel = this.wrap.createChild({
47437                 tag: 'div',
47438                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47439             }, this.el
47440         );
47441             
47442         this.svgID = Roo.id();
47443         this.svgEl = this.signPanel.createChild({
47444               xmlns : 'http://www.w3.org/2000/svg',
47445               tag : 'svg',
47446               id : this.svgID + "-svg",
47447               width: this.width,
47448               height: this.height,
47449               viewBox: '0 0 '+this.width+' '+this.height,
47450               cn : [
47451                 {
47452                     tag: "rect",
47453                     id: this.svgID + "-svg-r",
47454                     width: this.width,
47455                     height: this.height,
47456                     fill: "#ffa"
47457                 },
47458                 {
47459                     tag: "line",
47460                     id: this.svgID + "-svg-l",
47461                     x1: "0", // start
47462                     y1: (this.height*0.8), // start set the line in 80% of height
47463                     x2: this.width, // end
47464                     y2: (this.height*0.8), // end set the line in 80% of height
47465                     'stroke': "#666",
47466                     'stroke-width': "1",
47467                     'stroke-dasharray': "3",
47468                     'shape-rendering': "crispEdges",
47469                     'pointer-events': "none"
47470                 },
47471                 {
47472                     tag: "path",
47473                     id: this.svgID + "-svg-p",
47474                     'stroke': "navy",
47475                     'stroke-width': "3",
47476                     'fill': "none",
47477                     'pointer-events': 'none'
47478                 }
47479               ]
47480         });
47481         this.createSVG();
47482         this.svgBox = this.svgEl.dom.getScreenCTM();
47483     },
47484     createSVG : function(){ 
47485         var svg = this.signPanel;
47486         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47487         var t = this;
47488
47489         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47490         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47491         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47492         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47493         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47494         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47495         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47496         
47497     },
47498     isTouchEvent : function(e){
47499         return e.type.match(/^touch/);
47500     },
47501     getCoords : function (e) {
47502         var pt    = this.svgEl.dom.createSVGPoint();
47503         pt.x = e.clientX; 
47504         pt.y = e.clientY;
47505         if (this.isTouchEvent(e)) {
47506             pt.x =  e.targetTouches[0].clientX 
47507             pt.y = e.targetTouches[0].clientY;
47508         }
47509         var a = this.svgEl.dom.getScreenCTM();
47510         var b = a.inverse();
47511         var mx = pt.matrixTransform(b);
47512         return mx.x + ',' + mx.y;
47513     },
47514     //mouse event headler 
47515     down : function (e) {
47516         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47517         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47518         
47519         this.isMouseDown = true;
47520         
47521         e.preventDefault();
47522     },
47523     move : function (e) {
47524         if (this.isMouseDown) {
47525             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47526             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47527         }
47528         
47529         e.preventDefault();
47530     },
47531     up : function (e) {
47532         this.isMouseDown = false;
47533         var sp = this.signatureTmp.split(' ');
47534         
47535         if(sp.length > 1){
47536             if(!sp[sp.length-2].match(/^L/)){
47537                 sp.pop();
47538                 sp.pop();
47539                 sp.push("");
47540                 this.signatureTmp = sp.join(" ");
47541             }
47542         }
47543         if(this.getValue() != this.signatureTmp){
47544             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47545             this.isConfirmed = false;
47546         }
47547         e.preventDefault();
47548     },
47549     
47550     /**
47551      * Protected method that will not generally be called directly. It
47552      * is called when the editor creates its toolbar. Override this method if you need to
47553      * add custom toolbar buttons.
47554      * @param {HtmlEditor} editor
47555      */
47556     createToolbar : function(editor){
47557          function btn(id, toggle, handler){
47558             var xid = fid + '-'+ id ;
47559             return {
47560                 id : xid,
47561                 cmd : id,
47562                 cls : 'x-btn-icon x-edit-'+id,
47563                 enableToggle:toggle !== false,
47564                 scope: editor, // was editor...
47565                 handler:handler||editor.relayBtnCmd,
47566                 clickEvent:'mousedown',
47567                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47568                 tabIndex:-1
47569             };
47570         }
47571         
47572         
47573         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47574         this.tb = tb;
47575         this.tb.add(
47576            {
47577                 cls : ' x-signature-btn x-signature-'+id,
47578                 scope: editor, // was editor...
47579                 handler: this.reset,
47580                 clickEvent:'mousedown',
47581                 text: this.labels.clear
47582             },
47583             {
47584                  xtype : 'Fill',
47585                  xns: Roo.Toolbar
47586             }, 
47587             {
47588                 cls : '  x-signature-btn x-signature-'+id,
47589                 scope: editor, // was editor...
47590                 handler: this.confirmHandler,
47591                 clickEvent:'mousedown',
47592                 text: this.labels.confirm
47593             }
47594         );
47595     
47596     },
47597     //public
47598     /**
47599      * when user is clicked confirm then show this image.....
47600      * 
47601      * @return {String} Image Data URI
47602      */
47603     getImageDataURI : function(){
47604         var svg = this.svgEl.dom.parentNode.innerHTML;
47605         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47606         return src; 
47607     },
47608     /**
47609      * 
47610      * @return {Boolean} this.isConfirmed
47611      */
47612     getConfirmed : function(){
47613         return this.isConfirmed;
47614     },
47615     /**
47616      * 
47617      * @return {Number} this.width
47618      */
47619     getWidth : function(){
47620         return this.width;
47621     },
47622     /**
47623      * 
47624      * @return {Number} this.height
47625      */
47626     getHeight : function(){
47627         return this.height;
47628     },
47629     // private
47630     getSignature : function(){
47631         return this.signatureTmp;
47632     },
47633     // private
47634     reset : function(){
47635         this.signatureTmp = '';
47636         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47637         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47638         this.isConfirmed = false;
47639         Roo.form.Signature.superclass.reset.call(this);
47640     },
47641     setSignature : function(s){
47642         this.signatureTmp = s;
47643         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47644         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47645         this.setValue(s);
47646         this.isConfirmed = false;
47647         Roo.form.Signature.superclass.reset.call(this);
47648     }, 
47649     test : function(){
47650 //        Roo.log(this.signPanel.dom.contentWindow.up())
47651     },
47652     //private
47653     setConfirmed : function(){
47654         
47655         
47656         
47657 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47658     },
47659     // private
47660     confirmHandler : function(){
47661         if(!this.getSignature()){
47662             return;
47663         }
47664         
47665         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47666         this.setValue(this.getSignature());
47667         this.isConfirmed = true;
47668         
47669         this.fireEvent('confirm', this);
47670     },
47671     // private
47672     // Subclasses should provide the validation implementation by overriding this
47673     validateValue : function(value){
47674         if(this.allowBlank){
47675             return true;
47676         }
47677         
47678         if(this.isConfirmed){
47679             return true;
47680         }
47681         return false;
47682     }
47683 });/*
47684  * Based on:
47685  * Ext JS Library 1.1.1
47686  * Copyright(c) 2006-2007, Ext JS, LLC.
47687  *
47688  * Originally Released Under LGPL - original licence link has changed is not relivant.
47689  *
47690  * Fork - LGPL
47691  * <script type="text/javascript">
47692  */
47693  
47694
47695 /**
47696  * @class Roo.form.ComboBox
47697  * @extends Roo.form.TriggerField
47698  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47699  * @constructor
47700  * Create a new ComboBox.
47701  * @param {Object} config Configuration options
47702  */
47703 Roo.form.Select = function(config){
47704     Roo.form.Select.superclass.constructor.call(this, config);
47705      
47706 };
47707
47708 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47709     /**
47710      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47711      */
47712     /**
47713      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47714      * rendering into an Roo.Editor, defaults to false)
47715      */
47716     /**
47717      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47718      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47719      */
47720     /**
47721      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47722      */
47723     /**
47724      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47725      * the dropdown list (defaults to undefined, with no header element)
47726      */
47727
47728      /**
47729      * @cfg {String/Roo.Template} tpl The template to use to render the output
47730      */
47731      
47732     // private
47733     defaultAutoCreate : {tag: "select"  },
47734     /**
47735      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47736      */
47737     listWidth: undefined,
47738     /**
47739      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47740      * mode = 'remote' or 'text' if mode = 'local')
47741      */
47742     displayField: undefined,
47743     /**
47744      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47745      * mode = 'remote' or 'value' if mode = 'local'). 
47746      * Note: use of a valueField requires the user make a selection
47747      * in order for a value to be mapped.
47748      */
47749     valueField: undefined,
47750     
47751     
47752     /**
47753      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47754      * field's data value (defaults to the underlying DOM element's name)
47755      */
47756     hiddenName: undefined,
47757     /**
47758      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47759      */
47760     listClass: '',
47761     /**
47762      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47763      */
47764     selectedClass: 'x-combo-selected',
47765     /**
47766      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47767      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47768      * which displays a downward arrow icon).
47769      */
47770     triggerClass : 'x-form-arrow-trigger',
47771     /**
47772      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47773      */
47774     shadow:'sides',
47775     /**
47776      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47777      * anchor positions (defaults to 'tl-bl')
47778      */
47779     listAlign: 'tl-bl?',
47780     /**
47781      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47782      */
47783     maxHeight: 300,
47784     /**
47785      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47786      * query specified by the allQuery config option (defaults to 'query')
47787      */
47788     triggerAction: 'query',
47789     /**
47790      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47791      * (defaults to 4, does not apply if editable = false)
47792      */
47793     minChars : 4,
47794     /**
47795      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47796      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47797      */
47798     typeAhead: false,
47799     /**
47800      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47801      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47802      */
47803     queryDelay: 500,
47804     /**
47805      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47806      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47807      */
47808     pageSize: 0,
47809     /**
47810      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47811      * when editable = true (defaults to false)
47812      */
47813     selectOnFocus:false,
47814     /**
47815      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47816      */
47817     queryParam: 'query',
47818     /**
47819      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47820      * when mode = 'remote' (defaults to 'Loading...')
47821      */
47822     loadingText: 'Loading...',
47823     /**
47824      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47825      */
47826     resizable: false,
47827     /**
47828      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47829      */
47830     handleHeight : 8,
47831     /**
47832      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47833      * traditional select (defaults to true)
47834      */
47835     editable: true,
47836     /**
47837      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47838      */
47839     allQuery: '',
47840     /**
47841      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47842      */
47843     mode: 'remote',
47844     /**
47845      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47846      * listWidth has a higher value)
47847      */
47848     minListWidth : 70,
47849     /**
47850      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47851      * allow the user to set arbitrary text into the field (defaults to false)
47852      */
47853     forceSelection:false,
47854     /**
47855      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47856      * if typeAhead = true (defaults to 250)
47857      */
47858     typeAheadDelay : 250,
47859     /**
47860      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47861      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47862      */
47863     valueNotFoundText : undefined,
47864     
47865     /**
47866      * @cfg {String} defaultValue The value displayed after loading the store.
47867      */
47868     defaultValue: '',
47869     
47870     /**
47871      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47872      */
47873     blockFocus : false,
47874     
47875     /**
47876      * @cfg {Boolean} disableClear Disable showing of clear button.
47877      */
47878     disableClear : false,
47879     /**
47880      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47881      */
47882     alwaysQuery : false,
47883     
47884     //private
47885     addicon : false,
47886     editicon: false,
47887     
47888     // element that contains real text value.. (when hidden is used..)
47889      
47890     // private
47891     onRender : function(ct, position){
47892         Roo.form.Field.prototype.onRender.call(this, ct, position);
47893         
47894         if(this.store){
47895             this.store.on('beforeload', this.onBeforeLoad, this);
47896             this.store.on('load', this.onLoad, this);
47897             this.store.on('loadexception', this.onLoadException, this);
47898             this.store.load({});
47899         }
47900         
47901         
47902         
47903     },
47904
47905     // private
47906     initEvents : function(){
47907         //Roo.form.ComboBox.superclass.initEvents.call(this);
47908  
47909     },
47910
47911     onDestroy : function(){
47912        
47913         if(this.store){
47914             this.store.un('beforeload', this.onBeforeLoad, this);
47915             this.store.un('load', this.onLoad, this);
47916             this.store.un('loadexception', this.onLoadException, this);
47917         }
47918         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47919     },
47920
47921     // private
47922     fireKey : function(e){
47923         if(e.isNavKeyPress() && !this.list.isVisible()){
47924             this.fireEvent("specialkey", this, e);
47925         }
47926     },
47927
47928     // private
47929     onResize: function(w, h){
47930         
47931         return; 
47932     
47933         
47934     },
47935
47936     /**
47937      * Allow or prevent the user from directly editing the field text.  If false is passed,
47938      * the user will only be able to select from the items defined in the dropdown list.  This method
47939      * is the runtime equivalent of setting the 'editable' config option at config time.
47940      * @param {Boolean} value True to allow the user to directly edit the field text
47941      */
47942     setEditable : function(value){
47943          
47944     },
47945
47946     // private
47947     onBeforeLoad : function(){
47948         
47949         Roo.log("Select before load");
47950         return;
47951     
47952         this.innerList.update(this.loadingText ?
47953                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47954         //this.restrictHeight();
47955         this.selectedIndex = -1;
47956     },
47957
47958     // private
47959     onLoad : function(){
47960
47961     
47962         var dom = this.el.dom;
47963         dom.innerHTML = '';
47964          var od = dom.ownerDocument;
47965          
47966         if (this.emptyText) {
47967             var op = od.createElement('option');
47968             op.setAttribute('value', '');
47969             op.innerHTML = String.format('{0}', this.emptyText);
47970             dom.appendChild(op);
47971         }
47972         if(this.store.getCount() > 0){
47973            
47974             var vf = this.valueField;
47975             var df = this.displayField;
47976             this.store.data.each(function(r) {
47977                 // which colmsn to use... testing - cdoe / title..
47978                 var op = od.createElement('option');
47979                 op.setAttribute('value', r.data[vf]);
47980                 op.innerHTML = String.format('{0}', r.data[df]);
47981                 dom.appendChild(op);
47982             });
47983             if (typeof(this.defaultValue != 'undefined')) {
47984                 this.setValue(this.defaultValue);
47985             }
47986             
47987              
47988         }else{
47989             //this.onEmptyResults();
47990         }
47991         //this.el.focus();
47992     },
47993     // private
47994     onLoadException : function()
47995     {
47996         dom.innerHTML = '';
47997             
47998         Roo.log("Select on load exception");
47999         return;
48000     
48001         this.collapse();
48002         Roo.log(this.store.reader.jsonData);
48003         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48004             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48005         }
48006         
48007         
48008     },
48009     // private
48010     onTypeAhead : function(){
48011          
48012     },
48013
48014     // private
48015     onSelect : function(record, index){
48016         Roo.log('on select?');
48017         return;
48018         if(this.fireEvent('beforeselect', this, record, index) !== false){
48019             this.setFromData(index > -1 ? record.data : false);
48020             this.collapse();
48021             this.fireEvent('select', this, record, index);
48022         }
48023     },
48024
48025     /**
48026      * Returns the currently selected field value or empty string if no value is set.
48027      * @return {String} value The selected value
48028      */
48029     getValue : function(){
48030         var dom = this.el.dom;
48031         this.value = dom.options[dom.selectedIndex].value;
48032         return this.value;
48033         
48034     },
48035
48036     /**
48037      * Clears any text/value currently set in the field
48038      */
48039     clearValue : function(){
48040         this.value = '';
48041         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48042         
48043     },
48044
48045     /**
48046      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48047      * will be displayed in the field.  If the value does not match the data value of an existing item,
48048      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48049      * Otherwise the field will be blank (although the value will still be set).
48050      * @param {String} value The value to match
48051      */
48052     setValue : function(v){
48053         var d = this.el.dom;
48054         for (var i =0; i < d.options.length;i++) {
48055             if (v == d.options[i].value) {
48056                 d.selectedIndex = i;
48057                 this.value = v;
48058                 return;
48059             }
48060         }
48061         this.clearValue();
48062     },
48063     /**
48064      * @property {Object} the last set data for the element
48065      */
48066     
48067     lastData : false,
48068     /**
48069      * Sets the value of the field based on a object which is related to the record format for the store.
48070      * @param {Object} value the value to set as. or false on reset?
48071      */
48072     setFromData : function(o){
48073         Roo.log('setfrom data?');
48074          
48075         
48076         
48077     },
48078     // private
48079     reset : function(){
48080         this.clearValue();
48081     },
48082     // private
48083     findRecord : function(prop, value){
48084         
48085         return false;
48086     
48087         var record;
48088         if(this.store.getCount() > 0){
48089             this.store.each(function(r){
48090                 if(r.data[prop] == value){
48091                     record = r;
48092                     return false;
48093                 }
48094                 return true;
48095             });
48096         }
48097         return record;
48098     },
48099     
48100     getName: function()
48101     {
48102         // returns hidden if it's set..
48103         if (!this.rendered) {return ''};
48104         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48105         
48106     },
48107      
48108
48109     
48110
48111     // private
48112     onEmptyResults : function(){
48113         Roo.log('empty results');
48114         //this.collapse();
48115     },
48116
48117     /**
48118      * Returns true if the dropdown list is expanded, else false.
48119      */
48120     isExpanded : function(){
48121         return false;
48122     },
48123
48124     /**
48125      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48126      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48127      * @param {String} value The data value of the item to select
48128      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48129      * selected item if it is not currently in view (defaults to true)
48130      * @return {Boolean} True if the value matched an item in the list, else false
48131      */
48132     selectByValue : function(v, scrollIntoView){
48133         Roo.log('select By Value');
48134         return false;
48135     
48136         if(v !== undefined && v !== null){
48137             var r = this.findRecord(this.valueField || this.displayField, v);
48138             if(r){
48139                 this.select(this.store.indexOf(r), scrollIntoView);
48140                 return true;
48141             }
48142         }
48143         return false;
48144     },
48145
48146     /**
48147      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48148      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48149      * @param {Number} index The zero-based index of the list item to select
48150      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48151      * selected item if it is not currently in view (defaults to true)
48152      */
48153     select : function(index, scrollIntoView){
48154         Roo.log('select ');
48155         return  ;
48156         
48157         this.selectedIndex = index;
48158         this.view.select(index);
48159         if(scrollIntoView !== false){
48160             var el = this.view.getNode(index);
48161             if(el){
48162                 this.innerList.scrollChildIntoView(el, false);
48163             }
48164         }
48165     },
48166
48167       
48168
48169     // private
48170     validateBlur : function(){
48171         
48172         return;
48173         
48174     },
48175
48176     // private
48177     initQuery : function(){
48178         this.doQuery(this.getRawValue());
48179     },
48180
48181     // private
48182     doForce : function(){
48183         if(this.el.dom.value.length > 0){
48184             this.el.dom.value =
48185                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48186              
48187         }
48188     },
48189
48190     /**
48191      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48192      * query allowing the query action to be canceled if needed.
48193      * @param {String} query The SQL query to execute
48194      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48195      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48196      * saved in the current store (defaults to false)
48197      */
48198     doQuery : function(q, forceAll){
48199         
48200         Roo.log('doQuery?');
48201         if(q === undefined || q === null){
48202             q = '';
48203         }
48204         var qe = {
48205             query: q,
48206             forceAll: forceAll,
48207             combo: this,
48208             cancel:false
48209         };
48210         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48211             return false;
48212         }
48213         q = qe.query;
48214         forceAll = qe.forceAll;
48215         if(forceAll === true || (q.length >= this.minChars)){
48216             if(this.lastQuery != q || this.alwaysQuery){
48217                 this.lastQuery = q;
48218                 if(this.mode == 'local'){
48219                     this.selectedIndex = -1;
48220                     if(forceAll){
48221                         this.store.clearFilter();
48222                     }else{
48223                         this.store.filter(this.displayField, q);
48224                     }
48225                     this.onLoad();
48226                 }else{
48227                     this.store.baseParams[this.queryParam] = q;
48228                     this.store.load({
48229                         params: this.getParams(q)
48230                     });
48231                     this.expand();
48232                 }
48233             }else{
48234                 this.selectedIndex = -1;
48235                 this.onLoad();   
48236             }
48237         }
48238     },
48239
48240     // private
48241     getParams : function(q){
48242         var p = {};
48243         //p[this.queryParam] = q;
48244         if(this.pageSize){
48245             p.start = 0;
48246             p.limit = this.pageSize;
48247         }
48248         return p;
48249     },
48250
48251     /**
48252      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48253      */
48254     collapse : function(){
48255         
48256     },
48257
48258     // private
48259     collapseIf : function(e){
48260         
48261     },
48262
48263     /**
48264      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48265      */
48266     expand : function(){
48267         
48268     } ,
48269
48270     // private
48271      
48272
48273     /** 
48274     * @cfg {Boolean} grow 
48275     * @hide 
48276     */
48277     /** 
48278     * @cfg {Number} growMin 
48279     * @hide 
48280     */
48281     /** 
48282     * @cfg {Number} growMax 
48283     * @hide 
48284     */
48285     /**
48286      * @hide
48287      * @method autoSize
48288      */
48289     
48290     setWidth : function()
48291     {
48292         
48293     },
48294     getResizeEl : function(){
48295         return this.el;
48296     }
48297 });//<script type="text/javasscript">
48298  
48299
48300 /**
48301  * @class Roo.DDView
48302  * A DnD enabled version of Roo.View.
48303  * @param {Element/String} container The Element in which to create the View.
48304  * @param {String} tpl The template string used to create the markup for each element of the View
48305  * @param {Object} config The configuration properties. These include all the config options of
48306  * {@link Roo.View} plus some specific to this class.<br>
48307  * <p>
48308  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48309  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48310  * <p>
48311  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48312 .x-view-drag-insert-above {
48313         border-top:1px dotted #3366cc;
48314 }
48315 .x-view-drag-insert-below {
48316         border-bottom:1px dotted #3366cc;
48317 }
48318 </code></pre>
48319  * 
48320  */
48321  
48322 Roo.DDView = function(container, tpl, config) {
48323     Roo.DDView.superclass.constructor.apply(this, arguments);
48324     this.getEl().setStyle("outline", "0px none");
48325     this.getEl().unselectable();
48326     if (this.dragGroup) {
48327                 this.setDraggable(this.dragGroup.split(","));
48328     }
48329     if (this.dropGroup) {
48330                 this.setDroppable(this.dropGroup.split(","));
48331     }
48332     if (this.deletable) {
48333         this.setDeletable();
48334     }
48335     this.isDirtyFlag = false;
48336         this.addEvents({
48337                 "drop" : true
48338         });
48339 };
48340
48341 Roo.extend(Roo.DDView, Roo.View, {
48342 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48343 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48344 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48345 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48346
48347         isFormField: true,
48348
48349         reset: Roo.emptyFn,
48350         
48351         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48352
48353         validate: function() {
48354                 return true;
48355         },
48356         
48357         destroy: function() {
48358                 this.purgeListeners();
48359                 this.getEl.removeAllListeners();
48360                 this.getEl().remove();
48361                 if (this.dragZone) {
48362                         if (this.dragZone.destroy) {
48363                                 this.dragZone.destroy();
48364                         }
48365                 }
48366                 if (this.dropZone) {
48367                         if (this.dropZone.destroy) {
48368                                 this.dropZone.destroy();
48369                         }
48370                 }
48371         },
48372
48373 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48374         getName: function() {
48375                 return this.name;
48376         },
48377
48378 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48379         setValue: function(v) {
48380                 if (!this.store) {
48381                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48382                 }
48383                 var data = {};
48384                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48385                 this.store.proxy = new Roo.data.MemoryProxy(data);
48386                 this.store.load();
48387         },
48388
48389 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48390         getValue: function() {
48391                 var result = '(';
48392                 this.store.each(function(rec) {
48393                         result += rec.id + ',';
48394                 });
48395                 return result.substr(0, result.length - 1) + ')';
48396         },
48397         
48398         getIds: function() {
48399                 var i = 0, result = new Array(this.store.getCount());
48400                 this.store.each(function(rec) {
48401                         result[i++] = rec.id;
48402                 });
48403                 return result;
48404         },
48405         
48406         isDirty: function() {
48407                 return this.isDirtyFlag;
48408         },
48409
48410 /**
48411  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48412  *      whole Element becomes the target, and this causes the drop gesture to append.
48413  */
48414     getTargetFromEvent : function(e) {
48415                 var target = e.getTarget();
48416                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48417                 target = target.parentNode;
48418                 }
48419                 if (!target) {
48420                         target = this.el.dom.lastChild || this.el.dom;
48421                 }
48422                 return target;
48423     },
48424
48425 /**
48426  *      Create the drag data which consists of an object which has the property "ddel" as
48427  *      the drag proxy element. 
48428  */
48429     getDragData : function(e) {
48430         var target = this.findItemFromChild(e.getTarget());
48431                 if(target) {
48432                         this.handleSelection(e);
48433                         var selNodes = this.getSelectedNodes();
48434             var dragData = {
48435                 source: this,
48436                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48437                 nodes: selNodes,
48438                 records: []
48439                         };
48440                         var selectedIndices = this.getSelectedIndexes();
48441                         for (var i = 0; i < selectedIndices.length; i++) {
48442                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48443                         }
48444                         if (selNodes.length == 1) {
48445                                 dragData.ddel = target.cloneNode(true); // the div element
48446                         } else {
48447                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48448                                 div.className = 'multi-proxy';
48449                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48450                                         div.appendChild(selNodes[i].cloneNode(true));
48451                                 }
48452                                 dragData.ddel = div;
48453                         }
48454             //console.log(dragData)
48455             //console.log(dragData.ddel.innerHTML)
48456                         return dragData;
48457                 }
48458         //console.log('nodragData')
48459                 return false;
48460     },
48461     
48462 /**     Specify to which ddGroup items in this DDView may be dragged. */
48463     setDraggable: function(ddGroup) {
48464         if (ddGroup instanceof Array) {
48465                 Roo.each(ddGroup, this.setDraggable, this);
48466                 return;
48467         }
48468         if (this.dragZone) {
48469                 this.dragZone.addToGroup(ddGroup);
48470         } else {
48471                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48472                                 containerScroll: true,
48473                                 ddGroup: ddGroup 
48474
48475                         });
48476 //                      Draggability implies selection. DragZone's mousedown selects the element.
48477                         if (!this.multiSelect) { this.singleSelect = true; }
48478
48479 //                      Wire the DragZone's handlers up to methods in *this*
48480                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48481                 }
48482     },
48483
48484 /**     Specify from which ddGroup this DDView accepts drops. */
48485     setDroppable: function(ddGroup) {
48486         if (ddGroup instanceof Array) {
48487                 Roo.each(ddGroup, this.setDroppable, this);
48488                 return;
48489         }
48490         if (this.dropZone) {
48491                 this.dropZone.addToGroup(ddGroup);
48492         } else {
48493                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48494                                 containerScroll: true,
48495                                 ddGroup: ddGroup
48496                         });
48497
48498 //                      Wire the DropZone's handlers up to methods in *this*
48499                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48500                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48501                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48502                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48503                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48504                 }
48505     },
48506
48507 /**     Decide whether to drop above or below a View node. */
48508     getDropPoint : function(e, n, dd){
48509         if (n == this.el.dom) { return "above"; }
48510                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48511                 var c = t + (b - t) / 2;
48512                 var y = Roo.lib.Event.getPageY(e);
48513                 if(y <= c) {
48514                         return "above";
48515                 }else{
48516                         return "below";
48517                 }
48518     },
48519
48520     onNodeEnter : function(n, dd, e, data){
48521                 return false;
48522     },
48523     
48524     onNodeOver : function(n, dd, e, data){
48525                 var pt = this.getDropPoint(e, n, dd);
48526                 // set the insert point style on the target node
48527                 var dragElClass = this.dropNotAllowed;
48528                 if (pt) {
48529                         var targetElClass;
48530                         if (pt == "above"){
48531                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48532                                 targetElClass = "x-view-drag-insert-above";
48533                         } else {
48534                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48535                                 targetElClass = "x-view-drag-insert-below";
48536                         }
48537                         if (this.lastInsertClass != targetElClass){
48538                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48539                                 this.lastInsertClass = targetElClass;
48540                         }
48541                 }
48542                 return dragElClass;
48543         },
48544
48545     onNodeOut : function(n, dd, e, data){
48546                 this.removeDropIndicators(n);
48547     },
48548
48549     onNodeDrop : function(n, dd, e, data){
48550         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48551                 return false;
48552         }
48553         var pt = this.getDropPoint(e, n, dd);
48554                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48555                 if (pt == "below") { insertAt++; }
48556                 for (var i = 0; i < data.records.length; i++) {
48557                         var r = data.records[i];
48558                         var dup = this.store.getById(r.id);
48559                         if (dup && (dd != this.dragZone)) {
48560                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48561                         } else {
48562                                 if (data.copy) {
48563                                         this.store.insert(insertAt++, r.copy());
48564                                 } else {
48565                                         data.source.isDirtyFlag = true;
48566                                         r.store.remove(r);
48567                                         this.store.insert(insertAt++, r);
48568                                 }
48569                                 this.isDirtyFlag = true;
48570                         }
48571                 }
48572                 this.dragZone.cachedTarget = null;
48573                 return true;
48574     },
48575
48576     removeDropIndicators : function(n){
48577                 if(n){
48578                         Roo.fly(n).removeClass([
48579                                 "x-view-drag-insert-above",
48580                                 "x-view-drag-insert-below"]);
48581                         this.lastInsertClass = "_noclass";
48582                 }
48583     },
48584
48585 /**
48586  *      Utility method. Add a delete option to the DDView's context menu.
48587  *      @param {String} imageUrl The URL of the "delete" icon image.
48588  */
48589         setDeletable: function(imageUrl) {
48590                 if (!this.singleSelect && !this.multiSelect) {
48591                         this.singleSelect = true;
48592                 }
48593                 var c = this.getContextMenu();
48594                 this.contextMenu.on("itemclick", function(item) {
48595                         switch (item.id) {
48596                                 case "delete":
48597                                         this.remove(this.getSelectedIndexes());
48598                                         break;
48599                         }
48600                 }, this);
48601                 this.contextMenu.add({
48602                         icon: imageUrl,
48603                         id: "delete",
48604                         text: 'Delete'
48605                 });
48606         },
48607         
48608 /**     Return the context menu for this DDView. */
48609         getContextMenu: function() {
48610                 if (!this.contextMenu) {
48611 //                      Create the View's context menu
48612                         this.contextMenu = new Roo.menu.Menu({
48613                                 id: this.id + "-contextmenu"
48614                         });
48615                         this.el.on("contextmenu", this.showContextMenu, this);
48616                 }
48617                 return this.contextMenu;
48618         },
48619         
48620         disableContextMenu: function() {
48621                 if (this.contextMenu) {
48622                         this.el.un("contextmenu", this.showContextMenu, this);
48623                 }
48624         },
48625
48626         showContextMenu: function(e, item) {
48627         item = this.findItemFromChild(e.getTarget());
48628                 if (item) {
48629                         e.stopEvent();
48630                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48631                         this.contextMenu.showAt(e.getXY());
48632             }
48633     },
48634
48635 /**
48636  *      Remove {@link Roo.data.Record}s at the specified indices.
48637  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48638  */
48639     remove: function(selectedIndices) {
48640                 selectedIndices = [].concat(selectedIndices);
48641                 for (var i = 0; i < selectedIndices.length; i++) {
48642                         var rec = this.store.getAt(selectedIndices[i]);
48643                         this.store.remove(rec);
48644                 }
48645     },
48646
48647 /**
48648  *      Double click fires the event, but also, if this is draggable, and there is only one other
48649  *      related DropZone, it transfers the selected node.
48650  */
48651     onDblClick : function(e){
48652         var item = this.findItemFromChild(e.getTarget());
48653         if(item){
48654             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48655                 return false;
48656             }
48657             if (this.dragGroup) {
48658                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48659                     while (targets.indexOf(this.dropZone) > -1) {
48660                             targets.remove(this.dropZone);
48661                                 }
48662                     if (targets.length == 1) {
48663                                         this.dragZone.cachedTarget = null;
48664                         var el = Roo.get(targets[0].getEl());
48665                         var box = el.getBox(true);
48666                         targets[0].onNodeDrop(el.dom, {
48667                                 target: el.dom,
48668                                 xy: [box.x, box.y + box.height - 1]
48669                         }, null, this.getDragData(e));
48670                     }
48671                 }
48672         }
48673     },
48674     
48675     handleSelection: function(e) {
48676                 this.dragZone.cachedTarget = null;
48677         var item = this.findItemFromChild(e.getTarget());
48678         if (!item) {
48679                 this.clearSelections(true);
48680                 return;
48681         }
48682                 if (item && (this.multiSelect || this.singleSelect)){
48683                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48684                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48685                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48686                                 this.unselect(item);
48687                         } else {
48688                                 this.select(item, this.multiSelect && e.ctrlKey);
48689                                 this.lastSelection = item;
48690                         }
48691                 }
48692     },
48693
48694     onItemClick : function(item, index, e){
48695                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48696                         return false;
48697                 }
48698                 return true;
48699     },
48700
48701     unselect : function(nodeInfo, suppressEvent){
48702                 var node = this.getNode(nodeInfo);
48703                 if(node && this.isSelected(node)){
48704                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48705                                 Roo.fly(node).removeClass(this.selectedClass);
48706                                 this.selections.remove(node);
48707                                 if(!suppressEvent){
48708                                         this.fireEvent("selectionchange", this, this.selections);
48709                                 }
48710                         }
48711                 }
48712     }
48713 });
48714 /*
48715  * Based on:
48716  * Ext JS Library 1.1.1
48717  * Copyright(c) 2006-2007, Ext JS, LLC.
48718  *
48719  * Originally Released Under LGPL - original licence link has changed is not relivant.
48720  *
48721  * Fork - LGPL
48722  * <script type="text/javascript">
48723  */
48724  
48725 /**
48726  * @class Roo.LayoutManager
48727  * @extends Roo.util.Observable
48728  * Base class for layout managers.
48729  */
48730 Roo.LayoutManager = function(container, config){
48731     Roo.LayoutManager.superclass.constructor.call(this);
48732     this.el = Roo.get(container);
48733     // ie scrollbar fix
48734     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48735         document.body.scroll = "no";
48736     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48737         this.el.position('relative');
48738     }
48739     this.id = this.el.id;
48740     this.el.addClass("x-layout-container");
48741     /** false to disable window resize monitoring @type Boolean */
48742     this.monitorWindowResize = true;
48743     this.regions = {};
48744     this.addEvents({
48745         /**
48746          * @event layout
48747          * Fires when a layout is performed. 
48748          * @param {Roo.LayoutManager} this
48749          */
48750         "layout" : true,
48751         /**
48752          * @event regionresized
48753          * Fires when the user resizes a region. 
48754          * @param {Roo.LayoutRegion} region The resized region
48755          * @param {Number} newSize The new size (width for east/west, height for north/south)
48756          */
48757         "regionresized" : true,
48758         /**
48759          * @event regioncollapsed
48760          * Fires when a region is collapsed. 
48761          * @param {Roo.LayoutRegion} region The collapsed region
48762          */
48763         "regioncollapsed" : true,
48764         /**
48765          * @event regionexpanded
48766          * Fires when a region is expanded.  
48767          * @param {Roo.LayoutRegion} region The expanded region
48768          */
48769         "regionexpanded" : true
48770     });
48771     this.updating = false;
48772     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48773 };
48774
48775 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48776     /**
48777      * Returns true if this layout is currently being updated
48778      * @return {Boolean}
48779      */
48780     isUpdating : function(){
48781         return this.updating; 
48782     },
48783     
48784     /**
48785      * Suspend the LayoutManager from doing auto-layouts while
48786      * making multiple add or remove calls
48787      */
48788     beginUpdate : function(){
48789         this.updating = true;    
48790     },
48791     
48792     /**
48793      * Restore auto-layouts and optionally disable the manager from performing a layout
48794      * @param {Boolean} noLayout true to disable a layout update 
48795      */
48796     endUpdate : function(noLayout){
48797         this.updating = false;
48798         if(!noLayout){
48799             this.layout();
48800         }    
48801     },
48802     
48803     layout: function(){
48804         
48805     },
48806     
48807     onRegionResized : function(region, newSize){
48808         this.fireEvent("regionresized", region, newSize);
48809         this.layout();
48810     },
48811     
48812     onRegionCollapsed : function(region){
48813         this.fireEvent("regioncollapsed", region);
48814     },
48815     
48816     onRegionExpanded : function(region){
48817         this.fireEvent("regionexpanded", region);
48818     },
48819         
48820     /**
48821      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48822      * performs box-model adjustments.
48823      * @return {Object} The size as an object {width: (the width), height: (the height)}
48824      */
48825     getViewSize : function(){
48826         var size;
48827         if(this.el.dom != document.body){
48828             size = this.el.getSize();
48829         }else{
48830             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48831         }
48832         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48833         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48834         return size;
48835     },
48836     
48837     /**
48838      * Returns the Element this layout is bound to.
48839      * @return {Roo.Element}
48840      */
48841     getEl : function(){
48842         return this.el;
48843     },
48844     
48845     /**
48846      * Returns the specified region.
48847      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48848      * @return {Roo.LayoutRegion}
48849      */
48850     getRegion : function(target){
48851         return this.regions[target.toLowerCase()];
48852     },
48853     
48854     onWindowResize : function(){
48855         if(this.monitorWindowResize){
48856             this.layout();
48857         }
48858     }
48859 });/*
48860  * Based on:
48861  * Ext JS Library 1.1.1
48862  * Copyright(c) 2006-2007, Ext JS, LLC.
48863  *
48864  * Originally Released Under LGPL - original licence link has changed is not relivant.
48865  *
48866  * Fork - LGPL
48867  * <script type="text/javascript">
48868  */
48869 /**
48870  * @class Roo.BorderLayout
48871  * @extends Roo.LayoutManager
48872  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48873  * please see: <br><br>
48874  * <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>
48875  * <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>
48876  * Example:
48877  <pre><code>
48878  var layout = new Roo.BorderLayout(document.body, {
48879     north: {
48880         initialSize: 25,
48881         titlebar: false
48882     },
48883     west: {
48884         split:true,
48885         initialSize: 200,
48886         minSize: 175,
48887         maxSize: 400,
48888         titlebar: true,
48889         collapsible: true
48890     },
48891     east: {
48892         split:true,
48893         initialSize: 202,
48894         minSize: 175,
48895         maxSize: 400,
48896         titlebar: true,
48897         collapsible: true
48898     },
48899     south: {
48900         split:true,
48901         initialSize: 100,
48902         minSize: 100,
48903         maxSize: 200,
48904         titlebar: true,
48905         collapsible: true
48906     },
48907     center: {
48908         titlebar: true,
48909         autoScroll:true,
48910         resizeTabs: true,
48911         minTabWidth: 50,
48912         preferredTabWidth: 150
48913     }
48914 });
48915
48916 // shorthand
48917 var CP = Roo.ContentPanel;
48918
48919 layout.beginUpdate();
48920 layout.add("north", new CP("north", "North"));
48921 layout.add("south", new CP("south", {title: "South", closable: true}));
48922 layout.add("west", new CP("west", {title: "West"}));
48923 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48924 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48925 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48926 layout.getRegion("center").showPanel("center1");
48927 layout.endUpdate();
48928 </code></pre>
48929
48930 <b>The container the layout is rendered into can be either the body element or any other element.
48931 If it is not the body element, the container needs to either be an absolute positioned element,
48932 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48933 the container size if it is not the body element.</b>
48934
48935 * @constructor
48936 * Create a new BorderLayout
48937 * @param {String/HTMLElement/Element} container The container this layout is bound to
48938 * @param {Object} config Configuration options
48939  */
48940 Roo.BorderLayout = function(container, config){
48941     config = config || {};
48942     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48943     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48944     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48945         var target = this.factory.validRegions[i];
48946         if(config[target]){
48947             this.addRegion(target, config[target]);
48948         }
48949     }
48950 };
48951
48952 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48953     /**
48954      * Creates and adds a new region if it doesn't already exist.
48955      * @param {String} target The target region key (north, south, east, west or center).
48956      * @param {Object} config The regions config object
48957      * @return {BorderLayoutRegion} The new region
48958      */
48959     addRegion : function(target, config){
48960         if(!this.regions[target]){
48961             var r = this.factory.create(target, this, config);
48962             this.bindRegion(target, r);
48963         }
48964         return this.regions[target];
48965     },
48966
48967     // private (kinda)
48968     bindRegion : function(name, r){
48969         this.regions[name] = r;
48970         r.on("visibilitychange", this.layout, this);
48971         r.on("paneladded", this.layout, this);
48972         r.on("panelremoved", this.layout, this);
48973         r.on("invalidated", this.layout, this);
48974         r.on("resized", this.onRegionResized, this);
48975         r.on("collapsed", this.onRegionCollapsed, this);
48976         r.on("expanded", this.onRegionExpanded, this);
48977     },
48978
48979     /**
48980      * Performs a layout update.
48981      */
48982     layout : function(){
48983         if(this.updating) return;
48984         var size = this.getViewSize();
48985         var w = size.width;
48986         var h = size.height;
48987         var centerW = w;
48988         var centerH = h;
48989         var centerY = 0;
48990         var centerX = 0;
48991         //var x = 0, y = 0;
48992
48993         var rs = this.regions;
48994         var north = rs["north"];
48995         var south = rs["south"]; 
48996         var west = rs["west"];
48997         var east = rs["east"];
48998         var center = rs["center"];
48999         //if(this.hideOnLayout){ // not supported anymore
49000             //c.el.setStyle("display", "none");
49001         //}
49002         if(north && north.isVisible()){
49003             var b = north.getBox();
49004             var m = north.getMargins();
49005             b.width = w - (m.left+m.right);
49006             b.x = m.left;
49007             b.y = m.top;
49008             centerY = b.height + b.y + m.bottom;
49009             centerH -= centerY;
49010             north.updateBox(this.safeBox(b));
49011         }
49012         if(south && south.isVisible()){
49013             var b = south.getBox();
49014             var m = south.getMargins();
49015             b.width = w - (m.left+m.right);
49016             b.x = m.left;
49017             var totalHeight = (b.height + m.top + m.bottom);
49018             b.y = h - totalHeight + m.top;
49019             centerH -= totalHeight;
49020             south.updateBox(this.safeBox(b));
49021         }
49022         if(west && west.isVisible()){
49023             var b = west.getBox();
49024             var m = west.getMargins();
49025             b.height = centerH - (m.top+m.bottom);
49026             b.x = m.left;
49027             b.y = centerY + m.top;
49028             var totalWidth = (b.width + m.left + m.right);
49029             centerX += totalWidth;
49030             centerW -= totalWidth;
49031             west.updateBox(this.safeBox(b));
49032         }
49033         if(east && east.isVisible()){
49034             var b = east.getBox();
49035             var m = east.getMargins();
49036             b.height = centerH - (m.top+m.bottom);
49037             var totalWidth = (b.width + m.left + m.right);
49038             b.x = w - totalWidth + m.left;
49039             b.y = centerY + m.top;
49040             centerW -= totalWidth;
49041             east.updateBox(this.safeBox(b));
49042         }
49043         if(center){
49044             var m = center.getMargins();
49045             var centerBox = {
49046                 x: centerX + m.left,
49047                 y: centerY + m.top,
49048                 width: centerW - (m.left+m.right),
49049                 height: centerH - (m.top+m.bottom)
49050             };
49051             //if(this.hideOnLayout){
49052                 //center.el.setStyle("display", "block");
49053             //}
49054             center.updateBox(this.safeBox(centerBox));
49055         }
49056         this.el.repaint();
49057         this.fireEvent("layout", this);
49058     },
49059
49060     // private
49061     safeBox : function(box){
49062         box.width = Math.max(0, box.width);
49063         box.height = Math.max(0, box.height);
49064         return box;
49065     },
49066
49067     /**
49068      * Adds a ContentPanel (or subclass) to this layout.
49069      * @param {String} target The target region key (north, south, east, west or center).
49070      * @param {Roo.ContentPanel} panel The panel to add
49071      * @return {Roo.ContentPanel} The added panel
49072      */
49073     add : function(target, panel){
49074          
49075         target = target.toLowerCase();
49076         return this.regions[target].add(panel);
49077     },
49078
49079     /**
49080      * Remove a ContentPanel (or subclass) to this layout.
49081      * @param {String} target The target region key (north, south, east, west or center).
49082      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49083      * @return {Roo.ContentPanel} The removed panel
49084      */
49085     remove : function(target, panel){
49086         target = target.toLowerCase();
49087         return this.regions[target].remove(panel);
49088     },
49089
49090     /**
49091      * Searches all regions for a panel with the specified id
49092      * @param {String} panelId
49093      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49094      */
49095     findPanel : function(panelId){
49096         var rs = this.regions;
49097         for(var target in rs){
49098             if(typeof rs[target] != "function"){
49099                 var p = rs[target].getPanel(panelId);
49100                 if(p){
49101                     return p;
49102                 }
49103             }
49104         }
49105         return null;
49106     },
49107
49108     /**
49109      * Searches all regions for a panel with the specified id and activates (shows) it.
49110      * @param {String/ContentPanel} panelId The panels id or the panel itself
49111      * @return {Roo.ContentPanel} The shown panel or null
49112      */
49113     showPanel : function(panelId) {
49114       var rs = this.regions;
49115       for(var target in rs){
49116          var r = rs[target];
49117          if(typeof r != "function"){
49118             if(r.hasPanel(panelId)){
49119                return r.showPanel(panelId);
49120             }
49121          }
49122       }
49123       return null;
49124    },
49125
49126    /**
49127      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49128      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49129      */
49130     restoreState : function(provider){
49131         if(!provider){
49132             provider = Roo.state.Manager;
49133         }
49134         var sm = new Roo.LayoutStateManager();
49135         sm.init(this, provider);
49136     },
49137
49138     /**
49139      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49140      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49141      * a valid ContentPanel config object.  Example:
49142      * <pre><code>
49143 // Create the main layout
49144 var layout = new Roo.BorderLayout('main-ct', {
49145     west: {
49146         split:true,
49147         minSize: 175,
49148         titlebar: true
49149     },
49150     center: {
49151         title:'Components'
49152     }
49153 }, 'main-ct');
49154
49155 // Create and add multiple ContentPanels at once via configs
49156 layout.batchAdd({
49157    west: {
49158        id: 'source-files',
49159        autoCreate:true,
49160        title:'Ext Source Files',
49161        autoScroll:true,
49162        fitToFrame:true
49163    },
49164    center : {
49165        el: cview,
49166        autoScroll:true,
49167        fitToFrame:true,
49168        toolbar: tb,
49169        resizeEl:'cbody'
49170    }
49171 });
49172 </code></pre>
49173      * @param {Object} regions An object containing ContentPanel configs by region name
49174      */
49175     batchAdd : function(regions){
49176         this.beginUpdate();
49177         for(var rname in regions){
49178             var lr = this.regions[rname];
49179             if(lr){
49180                 this.addTypedPanels(lr, regions[rname]);
49181             }
49182         }
49183         this.endUpdate();
49184     },
49185
49186     // private
49187     addTypedPanels : function(lr, ps){
49188         if(typeof ps == 'string'){
49189             lr.add(new Roo.ContentPanel(ps));
49190         }
49191         else if(ps instanceof Array){
49192             for(var i =0, len = ps.length; i < len; i++){
49193                 this.addTypedPanels(lr, ps[i]);
49194             }
49195         }
49196         else if(!ps.events){ // raw config?
49197             var el = ps.el;
49198             delete ps.el; // prevent conflict
49199             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49200         }
49201         else {  // panel object assumed!
49202             lr.add(ps);
49203         }
49204     },
49205     /**
49206      * Adds a xtype elements to the layout.
49207      * <pre><code>
49208
49209 layout.addxtype({
49210        xtype : 'ContentPanel',
49211        region: 'west',
49212        items: [ .... ]
49213    }
49214 );
49215
49216 layout.addxtype({
49217         xtype : 'NestedLayoutPanel',
49218         region: 'west',
49219         layout: {
49220            center: { },
49221            west: { }   
49222         },
49223         items : [ ... list of content panels or nested layout panels.. ]
49224    }
49225 );
49226 </code></pre>
49227      * @param {Object} cfg Xtype definition of item to add.
49228      */
49229     addxtype : function(cfg)
49230     {
49231         // basically accepts a pannel...
49232         // can accept a layout region..!?!?
49233         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49234         
49235         if (!cfg.xtype.match(/Panel$/)) {
49236             return false;
49237         }
49238         var ret = false;
49239         
49240         if (typeof(cfg.region) == 'undefined') {
49241             Roo.log("Failed to add Panel, region was not set");
49242             Roo.log(cfg);
49243             return false;
49244         }
49245         var region = cfg.region;
49246         delete cfg.region;
49247         
49248           
49249         var xitems = [];
49250         if (cfg.items) {
49251             xitems = cfg.items;
49252             delete cfg.items;
49253         }
49254         var nb = false;
49255         
49256         switch(cfg.xtype) 
49257         {
49258             case 'ContentPanel':  // ContentPanel (el, cfg)
49259             case 'ScrollPanel':  // ContentPanel (el, cfg)
49260             case 'ViewPanel': 
49261                 if(cfg.autoCreate) {
49262                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49263                 } else {
49264                     var el = this.el.createChild();
49265                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49266                 }
49267                 
49268                 this.add(region, ret);
49269                 break;
49270             
49271             
49272             case 'TreePanel': // our new panel!
49273                 cfg.el = this.el.createChild();
49274                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49275                 this.add(region, ret);
49276                 break;
49277             
49278             case 'NestedLayoutPanel': 
49279                 // create a new Layout (which is  a Border Layout...
49280                 var el = this.el.createChild();
49281                 var clayout = cfg.layout;
49282                 delete cfg.layout;
49283                 clayout.items   = clayout.items  || [];
49284                 // replace this exitems with the clayout ones..
49285                 xitems = clayout.items;
49286                  
49287                 
49288                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49289                     cfg.background = false;
49290                 }
49291                 var layout = new Roo.BorderLayout(el, clayout);
49292                 
49293                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49294                 //console.log('adding nested layout panel '  + cfg.toSource());
49295                 this.add(region, ret);
49296                 nb = {}; /// find first...
49297                 break;
49298                 
49299             case 'GridPanel': 
49300             
49301                 // needs grid and region
49302                 
49303                 //var el = this.getRegion(region).el.createChild();
49304                 var el = this.el.createChild();
49305                 // create the grid first...
49306                 
49307                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49308                 delete cfg.grid;
49309                 if (region == 'center' && this.active ) {
49310                     cfg.background = false;
49311                 }
49312                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49313                 
49314                 this.add(region, ret);
49315                 if (cfg.background) {
49316                     ret.on('activate', function(gp) {
49317                         if (!gp.grid.rendered) {
49318                             gp.grid.render();
49319                         }
49320                     });
49321                 } else {
49322                     grid.render();
49323                 }
49324                 break;
49325            
49326            
49327            
49328                 
49329                 
49330                 
49331             default:
49332                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49333                     
49334                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49335                     this.add(region, ret);
49336                 } else {
49337                 
49338                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49339                     return null;
49340                 }
49341                 
49342              // GridPanel (grid, cfg)
49343             
49344         }
49345         this.beginUpdate();
49346         // add children..
49347         var region = '';
49348         var abn = {};
49349         Roo.each(xitems, function(i)  {
49350             region = nb && i.region ? i.region : false;
49351             
49352             var add = ret.addxtype(i);
49353            
49354             if (region) {
49355                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49356                 if (!i.background) {
49357                     abn[region] = nb[region] ;
49358                 }
49359             }
49360             
49361         });
49362         this.endUpdate();
49363
49364         // make the last non-background panel active..
49365         //if (nb) { Roo.log(abn); }
49366         if (nb) {
49367             
49368             for(var r in abn) {
49369                 region = this.getRegion(r);
49370                 if (region) {
49371                     // tried using nb[r], but it does not work..
49372                      
49373                     region.showPanel(abn[r]);
49374                    
49375                 }
49376             }
49377         }
49378         return ret;
49379         
49380     }
49381 });
49382
49383 /**
49384  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49385  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49386  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49387  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49388  * <pre><code>
49389 // shorthand
49390 var CP = Roo.ContentPanel;
49391
49392 var layout = Roo.BorderLayout.create({
49393     north: {
49394         initialSize: 25,
49395         titlebar: false,
49396         panels: [new CP("north", "North")]
49397     },
49398     west: {
49399         split:true,
49400         initialSize: 200,
49401         minSize: 175,
49402         maxSize: 400,
49403         titlebar: true,
49404         collapsible: true,
49405         panels: [new CP("west", {title: "West"})]
49406     },
49407     east: {
49408         split:true,
49409         initialSize: 202,
49410         minSize: 175,
49411         maxSize: 400,
49412         titlebar: true,
49413         collapsible: true,
49414         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49415     },
49416     south: {
49417         split:true,
49418         initialSize: 100,
49419         minSize: 100,
49420         maxSize: 200,
49421         titlebar: true,
49422         collapsible: true,
49423         panels: [new CP("south", {title: "South", closable: true})]
49424     },
49425     center: {
49426         titlebar: true,
49427         autoScroll:true,
49428         resizeTabs: true,
49429         minTabWidth: 50,
49430         preferredTabWidth: 150,
49431         panels: [
49432             new CP("center1", {title: "Close Me", closable: true}),
49433             new CP("center2", {title: "Center Panel", closable: false})
49434         ]
49435     }
49436 }, document.body);
49437
49438 layout.getRegion("center").showPanel("center1");
49439 </code></pre>
49440  * @param config
49441  * @param targetEl
49442  */
49443 Roo.BorderLayout.create = function(config, targetEl){
49444     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49445     layout.beginUpdate();
49446     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49447     for(var j = 0, jlen = regions.length; j < jlen; j++){
49448         var lr = regions[j];
49449         if(layout.regions[lr] && config[lr].panels){
49450             var r = layout.regions[lr];
49451             var ps = config[lr].panels;
49452             layout.addTypedPanels(r, ps);
49453         }
49454     }
49455     layout.endUpdate();
49456     return layout;
49457 };
49458
49459 // private
49460 Roo.BorderLayout.RegionFactory = {
49461     // private
49462     validRegions : ["north","south","east","west","center"],
49463
49464     // private
49465     create : function(target, mgr, config){
49466         target = target.toLowerCase();
49467         if(config.lightweight || config.basic){
49468             return new Roo.BasicLayoutRegion(mgr, config, target);
49469         }
49470         switch(target){
49471             case "north":
49472                 return new Roo.NorthLayoutRegion(mgr, config);
49473             case "south":
49474                 return new Roo.SouthLayoutRegion(mgr, config);
49475             case "east":
49476                 return new Roo.EastLayoutRegion(mgr, config);
49477             case "west":
49478                 return new Roo.WestLayoutRegion(mgr, config);
49479             case "center":
49480                 return new Roo.CenterLayoutRegion(mgr, config);
49481         }
49482         throw 'Layout region "'+target+'" not supported.';
49483     }
49484 };/*
49485  * Based on:
49486  * Ext JS Library 1.1.1
49487  * Copyright(c) 2006-2007, Ext JS, LLC.
49488  *
49489  * Originally Released Under LGPL - original licence link has changed is not relivant.
49490  *
49491  * Fork - LGPL
49492  * <script type="text/javascript">
49493  */
49494  
49495 /**
49496  * @class Roo.BasicLayoutRegion
49497  * @extends Roo.util.Observable
49498  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49499  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49500  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49501  */
49502 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49503     this.mgr = mgr;
49504     this.position  = pos;
49505     this.events = {
49506         /**
49507          * @scope Roo.BasicLayoutRegion
49508          */
49509         
49510         /**
49511          * @event beforeremove
49512          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49513          * @param {Roo.LayoutRegion} this
49514          * @param {Roo.ContentPanel} panel The panel
49515          * @param {Object} e The cancel event object
49516          */
49517         "beforeremove" : true,
49518         /**
49519          * @event invalidated
49520          * Fires when the layout for this region is changed.
49521          * @param {Roo.LayoutRegion} this
49522          */
49523         "invalidated" : true,
49524         /**
49525          * @event visibilitychange
49526          * Fires when this region is shown or hidden 
49527          * @param {Roo.LayoutRegion} this
49528          * @param {Boolean} visibility true or false
49529          */
49530         "visibilitychange" : true,
49531         /**
49532          * @event paneladded
49533          * Fires when a panel is added. 
49534          * @param {Roo.LayoutRegion} this
49535          * @param {Roo.ContentPanel} panel The panel
49536          */
49537         "paneladded" : true,
49538         /**
49539          * @event panelremoved
49540          * Fires when a panel is removed. 
49541          * @param {Roo.LayoutRegion} this
49542          * @param {Roo.ContentPanel} panel The panel
49543          */
49544         "panelremoved" : true,
49545         /**
49546          * @event collapsed
49547          * Fires when this region is collapsed.
49548          * @param {Roo.LayoutRegion} this
49549          */
49550         "collapsed" : true,
49551         /**
49552          * @event expanded
49553          * Fires when this region is expanded.
49554          * @param {Roo.LayoutRegion} this
49555          */
49556         "expanded" : true,
49557         /**
49558          * @event slideshow
49559          * Fires when this region is slid into view.
49560          * @param {Roo.LayoutRegion} this
49561          */
49562         "slideshow" : true,
49563         /**
49564          * @event slidehide
49565          * Fires when this region slides out of view. 
49566          * @param {Roo.LayoutRegion} this
49567          */
49568         "slidehide" : true,
49569         /**
49570          * @event panelactivated
49571          * Fires when a panel is activated. 
49572          * @param {Roo.LayoutRegion} this
49573          * @param {Roo.ContentPanel} panel The activated panel
49574          */
49575         "panelactivated" : true,
49576         /**
49577          * @event resized
49578          * Fires when the user resizes this region. 
49579          * @param {Roo.LayoutRegion} this
49580          * @param {Number} newSize The new size (width for east/west, height for north/south)
49581          */
49582         "resized" : true
49583     };
49584     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49585     this.panels = new Roo.util.MixedCollection();
49586     this.panels.getKey = this.getPanelId.createDelegate(this);
49587     this.box = null;
49588     this.activePanel = null;
49589     // ensure listeners are added...
49590     
49591     if (config.listeners || config.events) {
49592         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49593             listeners : config.listeners || {},
49594             events : config.events || {}
49595         });
49596     }
49597     
49598     if(skipConfig !== true){
49599         this.applyConfig(config);
49600     }
49601 };
49602
49603 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49604     getPanelId : function(p){
49605         return p.getId();
49606     },
49607     
49608     applyConfig : function(config){
49609         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49610         this.config = config;
49611         
49612     },
49613     
49614     /**
49615      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49616      * the width, for horizontal (north, south) the height.
49617      * @param {Number} newSize The new width or height
49618      */
49619     resizeTo : function(newSize){
49620         var el = this.el ? this.el :
49621                  (this.activePanel ? this.activePanel.getEl() : null);
49622         if(el){
49623             switch(this.position){
49624                 case "east":
49625                 case "west":
49626                     el.setWidth(newSize);
49627                     this.fireEvent("resized", this, newSize);
49628                 break;
49629                 case "north":
49630                 case "south":
49631                     el.setHeight(newSize);
49632                     this.fireEvent("resized", this, newSize);
49633                 break;                
49634             }
49635         }
49636     },
49637     
49638     getBox : function(){
49639         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49640     },
49641     
49642     getMargins : function(){
49643         return this.margins;
49644     },
49645     
49646     updateBox : function(box){
49647         this.box = box;
49648         var el = this.activePanel.getEl();
49649         el.dom.style.left = box.x + "px";
49650         el.dom.style.top = box.y + "px";
49651         this.activePanel.setSize(box.width, box.height);
49652     },
49653     
49654     /**
49655      * Returns the container element for this region.
49656      * @return {Roo.Element}
49657      */
49658     getEl : function(){
49659         return this.activePanel;
49660     },
49661     
49662     /**
49663      * Returns true if this region is currently visible.
49664      * @return {Boolean}
49665      */
49666     isVisible : function(){
49667         return this.activePanel ? true : false;
49668     },
49669     
49670     setActivePanel : function(panel){
49671         panel = this.getPanel(panel);
49672         if(this.activePanel && this.activePanel != panel){
49673             this.activePanel.setActiveState(false);
49674             this.activePanel.getEl().setLeftTop(-10000,-10000);
49675         }
49676         this.activePanel = panel;
49677         panel.setActiveState(true);
49678         if(this.box){
49679             panel.setSize(this.box.width, this.box.height);
49680         }
49681         this.fireEvent("panelactivated", this, panel);
49682         this.fireEvent("invalidated");
49683     },
49684     
49685     /**
49686      * Show the specified panel.
49687      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49688      * @return {Roo.ContentPanel} The shown panel or null
49689      */
49690     showPanel : function(panel){
49691         if(panel = this.getPanel(panel)){
49692             this.setActivePanel(panel);
49693         }
49694         return panel;
49695     },
49696     
49697     /**
49698      * Get the active panel for this region.
49699      * @return {Roo.ContentPanel} The active panel or null
49700      */
49701     getActivePanel : function(){
49702         return this.activePanel;
49703     },
49704     
49705     /**
49706      * Add the passed ContentPanel(s)
49707      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49708      * @return {Roo.ContentPanel} The panel added (if only one was added)
49709      */
49710     add : function(panel){
49711         if(arguments.length > 1){
49712             for(var i = 0, len = arguments.length; i < len; i++) {
49713                 this.add(arguments[i]);
49714             }
49715             return null;
49716         }
49717         if(this.hasPanel(panel)){
49718             this.showPanel(panel);
49719             return panel;
49720         }
49721         var el = panel.getEl();
49722         if(el.dom.parentNode != this.mgr.el.dom){
49723             this.mgr.el.dom.appendChild(el.dom);
49724         }
49725         if(panel.setRegion){
49726             panel.setRegion(this);
49727         }
49728         this.panels.add(panel);
49729         el.setStyle("position", "absolute");
49730         if(!panel.background){
49731             this.setActivePanel(panel);
49732             if(this.config.initialSize && this.panels.getCount()==1){
49733                 this.resizeTo(this.config.initialSize);
49734             }
49735         }
49736         this.fireEvent("paneladded", this, panel);
49737         return panel;
49738     },
49739     
49740     /**
49741      * Returns true if the panel is in this region.
49742      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49743      * @return {Boolean}
49744      */
49745     hasPanel : function(panel){
49746         if(typeof panel == "object"){ // must be panel obj
49747             panel = panel.getId();
49748         }
49749         return this.getPanel(panel) ? true : false;
49750     },
49751     
49752     /**
49753      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49754      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49755      * @param {Boolean} preservePanel Overrides the config preservePanel option
49756      * @return {Roo.ContentPanel} The panel that was removed
49757      */
49758     remove : function(panel, preservePanel){
49759         panel = this.getPanel(panel);
49760         if(!panel){
49761             return null;
49762         }
49763         var e = {};
49764         this.fireEvent("beforeremove", this, panel, e);
49765         if(e.cancel === true){
49766             return null;
49767         }
49768         var panelId = panel.getId();
49769         this.panels.removeKey(panelId);
49770         return panel;
49771     },
49772     
49773     /**
49774      * Returns the panel specified or null if it's not in this region.
49775      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49776      * @return {Roo.ContentPanel}
49777      */
49778     getPanel : function(id){
49779         if(typeof id == "object"){ // must be panel obj
49780             return id;
49781         }
49782         return this.panels.get(id);
49783     },
49784     
49785     /**
49786      * Returns this regions position (north/south/east/west/center).
49787      * @return {String} 
49788      */
49789     getPosition: function(){
49790         return this.position;    
49791     }
49792 });/*
49793  * Based on:
49794  * Ext JS Library 1.1.1
49795  * Copyright(c) 2006-2007, Ext JS, LLC.
49796  *
49797  * Originally Released Under LGPL - original licence link has changed is not relivant.
49798  *
49799  * Fork - LGPL
49800  * <script type="text/javascript">
49801  */
49802  
49803 /**
49804  * @class Roo.LayoutRegion
49805  * @extends Roo.BasicLayoutRegion
49806  * This class represents a region in a layout manager.
49807  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49808  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49809  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49810  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49811  * @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})
49812  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49813  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49814  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49815  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49816  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49817  * @cfg {String}    title           The title for the region (overrides panel titles)
49818  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49819  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49820  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49821  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49822  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49823  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49824  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49825  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49826  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49827  * @cfg {Boolean}   showPin         True to show a pin button
49828  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49829  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49830  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49831  * @cfg {Number}    width           For East/West panels
49832  * @cfg {Number}    height          For North/South panels
49833  * @cfg {Boolean}   split           To show the splitter
49834  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49835  */
49836 Roo.LayoutRegion = function(mgr, config, pos){
49837     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49838     var dh = Roo.DomHelper;
49839     /** This region's container element 
49840     * @type Roo.Element */
49841     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49842     /** This region's title element 
49843     * @type Roo.Element */
49844
49845     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49846         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49847         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49848     ]}, true);
49849     this.titleEl.enableDisplayMode();
49850     /** This region's title text element 
49851     * @type HTMLElement */
49852     this.titleTextEl = this.titleEl.dom.firstChild;
49853     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49854     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49855     this.closeBtn.enableDisplayMode();
49856     this.closeBtn.on("click", this.closeClicked, this);
49857     this.closeBtn.hide();
49858
49859     this.createBody(config);
49860     this.visible = true;
49861     this.collapsed = false;
49862
49863     if(config.hideWhenEmpty){
49864         this.hide();
49865         this.on("paneladded", this.validateVisibility, this);
49866         this.on("panelremoved", this.validateVisibility, this);
49867     }
49868     this.applyConfig(config);
49869 };
49870
49871 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49872
49873     createBody : function(){
49874         /** This region's body element 
49875         * @type Roo.Element */
49876         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49877     },
49878
49879     applyConfig : function(c){
49880         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49881             var dh = Roo.DomHelper;
49882             if(c.titlebar !== false){
49883                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49884                 this.collapseBtn.on("click", this.collapse, this);
49885                 this.collapseBtn.enableDisplayMode();
49886
49887                 if(c.showPin === true || this.showPin){
49888                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49889                     this.stickBtn.enableDisplayMode();
49890                     this.stickBtn.on("click", this.expand, this);
49891                     this.stickBtn.hide();
49892                 }
49893             }
49894             /** This region's collapsed element
49895             * @type Roo.Element */
49896             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49897                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49898             ]}, true);
49899             if(c.floatable !== false){
49900                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49901                this.collapsedEl.on("click", this.collapseClick, this);
49902             }
49903
49904             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49905                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49906                    id: "message", unselectable: "on", style:{"float":"left"}});
49907                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49908              }
49909             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49910             this.expandBtn.on("click", this.expand, this);
49911         }
49912         if(this.collapseBtn){
49913             this.collapseBtn.setVisible(c.collapsible == true);
49914         }
49915         this.cmargins = c.cmargins || this.cmargins ||
49916                          (this.position == "west" || this.position == "east" ?
49917                              {top: 0, left: 2, right:2, bottom: 0} :
49918                              {top: 2, left: 0, right:0, bottom: 2});
49919         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49920         this.bottomTabs = c.tabPosition != "top";
49921         this.autoScroll = c.autoScroll || false;
49922         if(this.autoScroll){
49923             this.bodyEl.setStyle("overflow", "auto");
49924         }else{
49925             this.bodyEl.setStyle("overflow", "hidden");
49926         }
49927         //if(c.titlebar !== false){
49928             if((!c.titlebar && !c.title) || c.titlebar === false){
49929                 this.titleEl.hide();
49930             }else{
49931                 this.titleEl.show();
49932                 if(c.title){
49933                     this.titleTextEl.innerHTML = c.title;
49934                 }
49935             }
49936         //}
49937         this.duration = c.duration || .30;
49938         this.slideDuration = c.slideDuration || .45;
49939         this.config = c;
49940         if(c.collapsed){
49941             this.collapse(true);
49942         }
49943         if(c.hidden){
49944             this.hide();
49945         }
49946     },
49947     /**
49948      * Returns true if this region is currently visible.
49949      * @return {Boolean}
49950      */
49951     isVisible : function(){
49952         return this.visible;
49953     },
49954
49955     /**
49956      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49957      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49958      */
49959     setCollapsedTitle : function(title){
49960         title = title || "&#160;";
49961         if(this.collapsedTitleTextEl){
49962             this.collapsedTitleTextEl.innerHTML = title;
49963         }
49964     },
49965
49966     getBox : function(){
49967         var b;
49968         if(!this.collapsed){
49969             b = this.el.getBox(false, true);
49970         }else{
49971             b = this.collapsedEl.getBox(false, true);
49972         }
49973         return b;
49974     },
49975
49976     getMargins : function(){
49977         return this.collapsed ? this.cmargins : this.margins;
49978     },
49979
49980     highlight : function(){
49981         this.el.addClass("x-layout-panel-dragover");
49982     },
49983
49984     unhighlight : function(){
49985         this.el.removeClass("x-layout-panel-dragover");
49986     },
49987
49988     updateBox : function(box){
49989         this.box = box;
49990         if(!this.collapsed){
49991             this.el.dom.style.left = box.x + "px";
49992             this.el.dom.style.top = box.y + "px";
49993             this.updateBody(box.width, box.height);
49994         }else{
49995             this.collapsedEl.dom.style.left = box.x + "px";
49996             this.collapsedEl.dom.style.top = box.y + "px";
49997             this.collapsedEl.setSize(box.width, box.height);
49998         }
49999         if(this.tabs){
50000             this.tabs.autoSizeTabs();
50001         }
50002     },
50003
50004     updateBody : function(w, h){
50005         if(w !== null){
50006             this.el.setWidth(w);
50007             w -= this.el.getBorderWidth("rl");
50008             if(this.config.adjustments){
50009                 w += this.config.adjustments[0];
50010             }
50011         }
50012         if(h !== null){
50013             this.el.setHeight(h);
50014             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50015             h -= this.el.getBorderWidth("tb");
50016             if(this.config.adjustments){
50017                 h += this.config.adjustments[1];
50018             }
50019             this.bodyEl.setHeight(h);
50020             if(this.tabs){
50021                 h = this.tabs.syncHeight(h);
50022             }
50023         }
50024         if(this.panelSize){
50025             w = w !== null ? w : this.panelSize.width;
50026             h = h !== null ? h : this.panelSize.height;
50027         }
50028         if(this.activePanel){
50029             var el = this.activePanel.getEl();
50030             w = w !== null ? w : el.getWidth();
50031             h = h !== null ? h : el.getHeight();
50032             this.panelSize = {width: w, height: h};
50033             this.activePanel.setSize(w, h);
50034         }
50035         if(Roo.isIE && this.tabs){
50036             this.tabs.el.repaint();
50037         }
50038     },
50039
50040     /**
50041      * Returns the container element for this region.
50042      * @return {Roo.Element}
50043      */
50044     getEl : function(){
50045         return this.el;
50046     },
50047
50048     /**
50049      * Hides this region.
50050      */
50051     hide : function(){
50052         if(!this.collapsed){
50053             this.el.dom.style.left = "-2000px";
50054             this.el.hide();
50055         }else{
50056             this.collapsedEl.dom.style.left = "-2000px";
50057             this.collapsedEl.hide();
50058         }
50059         this.visible = false;
50060         this.fireEvent("visibilitychange", this, false);
50061     },
50062
50063     /**
50064      * Shows this region if it was previously hidden.
50065      */
50066     show : function(){
50067         if(!this.collapsed){
50068             this.el.show();
50069         }else{
50070             this.collapsedEl.show();
50071         }
50072         this.visible = true;
50073         this.fireEvent("visibilitychange", this, true);
50074     },
50075
50076     closeClicked : function(){
50077         if(this.activePanel){
50078             this.remove(this.activePanel);
50079         }
50080     },
50081
50082     collapseClick : function(e){
50083         if(this.isSlid){
50084            e.stopPropagation();
50085            this.slideIn();
50086         }else{
50087            e.stopPropagation();
50088            this.slideOut();
50089         }
50090     },
50091
50092     /**
50093      * Collapses this region.
50094      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50095      */
50096     collapse : function(skipAnim){
50097         if(this.collapsed) return;
50098         this.collapsed = true;
50099         if(this.split){
50100             this.split.el.hide();
50101         }
50102         if(this.config.animate && skipAnim !== true){
50103             this.fireEvent("invalidated", this);
50104             this.animateCollapse();
50105         }else{
50106             this.el.setLocation(-20000,-20000);
50107             this.el.hide();
50108             this.collapsedEl.show();
50109             this.fireEvent("collapsed", this);
50110             this.fireEvent("invalidated", this);
50111         }
50112     },
50113
50114     animateCollapse : function(){
50115         // overridden
50116     },
50117
50118     /**
50119      * Expands this region if it was previously collapsed.
50120      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50121      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50122      */
50123     expand : function(e, skipAnim){
50124         if(e) e.stopPropagation();
50125         if(!this.collapsed || this.el.hasActiveFx()) return;
50126         if(this.isSlid){
50127             this.afterSlideIn();
50128             skipAnim = true;
50129         }
50130         this.collapsed = false;
50131         if(this.config.animate && skipAnim !== true){
50132             this.animateExpand();
50133         }else{
50134             this.el.show();
50135             if(this.split){
50136                 this.split.el.show();
50137             }
50138             this.collapsedEl.setLocation(-2000,-2000);
50139             this.collapsedEl.hide();
50140             this.fireEvent("invalidated", this);
50141             this.fireEvent("expanded", this);
50142         }
50143     },
50144
50145     animateExpand : function(){
50146         // overridden
50147     },
50148
50149     initTabs : function()
50150     {
50151         this.bodyEl.setStyle("overflow", "hidden");
50152         var ts = new Roo.TabPanel(
50153                 this.bodyEl.dom,
50154                 {
50155                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50156                     disableTooltips: this.config.disableTabTips,
50157                     toolbar : this.config.toolbar
50158                 }
50159         );
50160         if(this.config.hideTabs){
50161             ts.stripWrap.setDisplayed(false);
50162         }
50163         this.tabs = ts;
50164         ts.resizeTabs = this.config.resizeTabs === true;
50165         ts.minTabWidth = this.config.minTabWidth || 40;
50166         ts.maxTabWidth = this.config.maxTabWidth || 250;
50167         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50168         ts.monitorResize = false;
50169         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50170         ts.bodyEl.addClass('x-layout-tabs-body');
50171         this.panels.each(this.initPanelAsTab, this);
50172     },
50173
50174     initPanelAsTab : function(panel){
50175         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50176                     this.config.closeOnTab && panel.isClosable());
50177         if(panel.tabTip !== undefined){
50178             ti.setTooltip(panel.tabTip);
50179         }
50180         ti.on("activate", function(){
50181               this.setActivePanel(panel);
50182         }, this);
50183         if(this.config.closeOnTab){
50184             ti.on("beforeclose", function(t, e){
50185                 e.cancel = true;
50186                 this.remove(panel);
50187             }, this);
50188         }
50189         return ti;
50190     },
50191
50192     updatePanelTitle : function(panel, title){
50193         if(this.activePanel == panel){
50194             this.updateTitle(title);
50195         }
50196         if(this.tabs){
50197             var ti = this.tabs.getTab(panel.getEl().id);
50198             ti.setText(title);
50199             if(panel.tabTip !== undefined){
50200                 ti.setTooltip(panel.tabTip);
50201             }
50202         }
50203     },
50204
50205     updateTitle : function(title){
50206         if(this.titleTextEl && !this.config.title){
50207             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50208         }
50209     },
50210
50211     setActivePanel : function(panel){
50212         panel = this.getPanel(panel);
50213         if(this.activePanel && this.activePanel != panel){
50214             this.activePanel.setActiveState(false);
50215         }
50216         this.activePanel = panel;
50217         panel.setActiveState(true);
50218         if(this.panelSize){
50219             panel.setSize(this.panelSize.width, this.panelSize.height);
50220         }
50221         if(this.closeBtn){
50222             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50223         }
50224         this.updateTitle(panel.getTitle());
50225         if(this.tabs){
50226             this.fireEvent("invalidated", this);
50227         }
50228         this.fireEvent("panelactivated", this, panel);
50229     },
50230
50231     /**
50232      * Shows the specified panel.
50233      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50234      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50235      */
50236     showPanel : function(panel){
50237         if(panel = this.getPanel(panel)){
50238             if(this.tabs){
50239                 var tab = this.tabs.getTab(panel.getEl().id);
50240                 if(tab.isHidden()){
50241                     this.tabs.unhideTab(tab.id);
50242                 }
50243                 tab.activate();
50244             }else{
50245                 this.setActivePanel(panel);
50246             }
50247         }
50248         return panel;
50249     },
50250
50251     /**
50252      * Get the active panel for this region.
50253      * @return {Roo.ContentPanel} The active panel or null
50254      */
50255     getActivePanel : function(){
50256         return this.activePanel;
50257     },
50258
50259     validateVisibility : function(){
50260         if(this.panels.getCount() < 1){
50261             this.updateTitle("&#160;");
50262             this.closeBtn.hide();
50263             this.hide();
50264         }else{
50265             if(!this.isVisible()){
50266                 this.show();
50267             }
50268         }
50269     },
50270
50271     /**
50272      * Adds the passed ContentPanel(s) to this region.
50273      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50274      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50275      */
50276     add : function(panel){
50277         if(arguments.length > 1){
50278             for(var i = 0, len = arguments.length; i < len; i++) {
50279                 this.add(arguments[i]);
50280             }
50281             return null;
50282         }
50283         if(this.hasPanel(panel)){
50284             this.showPanel(panel);
50285             return panel;
50286         }
50287         panel.setRegion(this);
50288         this.panels.add(panel);
50289         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50290             this.bodyEl.dom.appendChild(panel.getEl().dom);
50291             if(panel.background !== true){
50292                 this.setActivePanel(panel);
50293             }
50294             this.fireEvent("paneladded", this, panel);
50295             return panel;
50296         }
50297         if(!this.tabs){
50298             this.initTabs();
50299         }else{
50300             this.initPanelAsTab(panel);
50301         }
50302         if(panel.background !== true){
50303             this.tabs.activate(panel.getEl().id);
50304         }
50305         this.fireEvent("paneladded", this, panel);
50306         return panel;
50307     },
50308
50309     /**
50310      * Hides the tab for the specified panel.
50311      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50312      */
50313     hidePanel : function(panel){
50314         if(this.tabs && (panel = this.getPanel(panel))){
50315             this.tabs.hideTab(panel.getEl().id);
50316         }
50317     },
50318
50319     /**
50320      * Unhides the tab for a previously hidden panel.
50321      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50322      */
50323     unhidePanel : function(panel){
50324         if(this.tabs && (panel = this.getPanel(panel))){
50325             this.tabs.unhideTab(panel.getEl().id);
50326         }
50327     },
50328
50329     clearPanels : function(){
50330         while(this.panels.getCount() > 0){
50331              this.remove(this.panels.first());
50332         }
50333     },
50334
50335     /**
50336      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50337      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50338      * @param {Boolean} preservePanel Overrides the config preservePanel option
50339      * @return {Roo.ContentPanel} The panel that was removed
50340      */
50341     remove : function(panel, preservePanel){
50342         panel = this.getPanel(panel);
50343         if(!panel){
50344             return null;
50345         }
50346         var e = {};
50347         this.fireEvent("beforeremove", this, panel, e);
50348         if(e.cancel === true){
50349             return null;
50350         }
50351         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50352         var panelId = panel.getId();
50353         this.panels.removeKey(panelId);
50354         if(preservePanel){
50355             document.body.appendChild(panel.getEl().dom);
50356         }
50357         if(this.tabs){
50358             this.tabs.removeTab(panel.getEl().id);
50359         }else if (!preservePanel){
50360             this.bodyEl.dom.removeChild(panel.getEl().dom);
50361         }
50362         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50363             var p = this.panels.first();
50364             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50365             tempEl.appendChild(p.getEl().dom);
50366             this.bodyEl.update("");
50367             this.bodyEl.dom.appendChild(p.getEl().dom);
50368             tempEl = null;
50369             this.updateTitle(p.getTitle());
50370             this.tabs = null;
50371             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50372             this.setActivePanel(p);
50373         }
50374         panel.setRegion(null);
50375         if(this.activePanel == panel){
50376             this.activePanel = null;
50377         }
50378         if(this.config.autoDestroy !== false && preservePanel !== true){
50379             try{panel.destroy();}catch(e){}
50380         }
50381         this.fireEvent("panelremoved", this, panel);
50382         return panel;
50383     },
50384
50385     /**
50386      * Returns the TabPanel component used by this region
50387      * @return {Roo.TabPanel}
50388      */
50389     getTabs : function(){
50390         return this.tabs;
50391     },
50392
50393     createTool : function(parentEl, className){
50394         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50395             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50396         btn.addClassOnOver("x-layout-tools-button-over");
50397         return btn;
50398     }
50399 });/*
50400  * Based on:
50401  * Ext JS Library 1.1.1
50402  * Copyright(c) 2006-2007, Ext JS, LLC.
50403  *
50404  * Originally Released Under LGPL - original licence link has changed is not relivant.
50405  *
50406  * Fork - LGPL
50407  * <script type="text/javascript">
50408  */
50409  
50410
50411
50412 /**
50413  * @class Roo.SplitLayoutRegion
50414  * @extends Roo.LayoutRegion
50415  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50416  */
50417 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50418     this.cursor = cursor;
50419     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50420 };
50421
50422 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50423     splitTip : "Drag to resize.",
50424     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50425     useSplitTips : false,
50426
50427     applyConfig : function(config){
50428         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50429         if(config.split){
50430             if(!this.split){
50431                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50432                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50433                 /** The SplitBar for this region 
50434                 * @type Roo.SplitBar */
50435                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50436                 this.split.on("moved", this.onSplitMove, this);
50437                 this.split.useShim = config.useShim === true;
50438                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50439                 if(this.useSplitTips){
50440                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50441                 }
50442                 if(config.collapsible){
50443                     this.split.el.on("dblclick", this.collapse,  this);
50444                 }
50445             }
50446             if(typeof config.minSize != "undefined"){
50447                 this.split.minSize = config.minSize;
50448             }
50449             if(typeof config.maxSize != "undefined"){
50450                 this.split.maxSize = config.maxSize;
50451             }
50452             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50453                 this.hideSplitter();
50454             }
50455         }
50456     },
50457
50458     getHMaxSize : function(){
50459          var cmax = this.config.maxSize || 10000;
50460          var center = this.mgr.getRegion("center");
50461          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50462     },
50463
50464     getVMaxSize : function(){
50465          var cmax = this.config.maxSize || 10000;
50466          var center = this.mgr.getRegion("center");
50467          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50468     },
50469
50470     onSplitMove : function(split, newSize){
50471         this.fireEvent("resized", this, newSize);
50472     },
50473     
50474     /** 
50475      * Returns the {@link Roo.SplitBar} for this region.
50476      * @return {Roo.SplitBar}
50477      */
50478     getSplitBar : function(){
50479         return this.split;
50480     },
50481     
50482     hide : function(){
50483         this.hideSplitter();
50484         Roo.SplitLayoutRegion.superclass.hide.call(this);
50485     },
50486
50487     hideSplitter : function(){
50488         if(this.split){
50489             this.split.el.setLocation(-2000,-2000);
50490             this.split.el.hide();
50491         }
50492     },
50493
50494     show : function(){
50495         if(this.split){
50496             this.split.el.show();
50497         }
50498         Roo.SplitLayoutRegion.superclass.show.call(this);
50499     },
50500     
50501     beforeSlide: function(){
50502         if(Roo.isGecko){// firefox overflow auto bug workaround
50503             this.bodyEl.clip();
50504             if(this.tabs) this.tabs.bodyEl.clip();
50505             if(this.activePanel){
50506                 this.activePanel.getEl().clip();
50507                 
50508                 if(this.activePanel.beforeSlide){
50509                     this.activePanel.beforeSlide();
50510                 }
50511             }
50512         }
50513     },
50514     
50515     afterSlide : function(){
50516         if(Roo.isGecko){// firefox overflow auto bug workaround
50517             this.bodyEl.unclip();
50518             if(this.tabs) this.tabs.bodyEl.unclip();
50519             if(this.activePanel){
50520                 this.activePanel.getEl().unclip();
50521                 if(this.activePanel.afterSlide){
50522                     this.activePanel.afterSlide();
50523                 }
50524             }
50525         }
50526     },
50527
50528     initAutoHide : function(){
50529         if(this.autoHide !== false){
50530             if(!this.autoHideHd){
50531                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50532                 this.autoHideHd = {
50533                     "mouseout": function(e){
50534                         if(!e.within(this.el, true)){
50535                             st.delay(500);
50536                         }
50537                     },
50538                     "mouseover" : function(e){
50539                         st.cancel();
50540                     },
50541                     scope : this
50542                 };
50543             }
50544             this.el.on(this.autoHideHd);
50545         }
50546     },
50547
50548     clearAutoHide : function(){
50549         if(this.autoHide !== false){
50550             this.el.un("mouseout", this.autoHideHd.mouseout);
50551             this.el.un("mouseover", this.autoHideHd.mouseover);
50552         }
50553     },
50554
50555     clearMonitor : function(){
50556         Roo.get(document).un("click", this.slideInIf, this);
50557     },
50558
50559     // these names are backwards but not changed for compat
50560     slideOut : function(){
50561         if(this.isSlid || this.el.hasActiveFx()){
50562             return;
50563         }
50564         this.isSlid = true;
50565         if(this.collapseBtn){
50566             this.collapseBtn.hide();
50567         }
50568         this.closeBtnState = this.closeBtn.getStyle('display');
50569         this.closeBtn.hide();
50570         if(this.stickBtn){
50571             this.stickBtn.show();
50572         }
50573         this.el.show();
50574         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50575         this.beforeSlide();
50576         this.el.setStyle("z-index", 10001);
50577         this.el.slideIn(this.getSlideAnchor(), {
50578             callback: function(){
50579                 this.afterSlide();
50580                 this.initAutoHide();
50581                 Roo.get(document).on("click", this.slideInIf, this);
50582                 this.fireEvent("slideshow", this);
50583             },
50584             scope: this,
50585             block: true
50586         });
50587     },
50588
50589     afterSlideIn : function(){
50590         this.clearAutoHide();
50591         this.isSlid = false;
50592         this.clearMonitor();
50593         this.el.setStyle("z-index", "");
50594         if(this.collapseBtn){
50595             this.collapseBtn.show();
50596         }
50597         this.closeBtn.setStyle('display', this.closeBtnState);
50598         if(this.stickBtn){
50599             this.stickBtn.hide();
50600         }
50601         this.fireEvent("slidehide", this);
50602     },
50603
50604     slideIn : function(cb){
50605         if(!this.isSlid || this.el.hasActiveFx()){
50606             Roo.callback(cb);
50607             return;
50608         }
50609         this.isSlid = false;
50610         this.beforeSlide();
50611         this.el.slideOut(this.getSlideAnchor(), {
50612             callback: function(){
50613                 this.el.setLeftTop(-10000, -10000);
50614                 this.afterSlide();
50615                 this.afterSlideIn();
50616                 Roo.callback(cb);
50617             },
50618             scope: this,
50619             block: true
50620         });
50621     },
50622     
50623     slideInIf : function(e){
50624         if(!e.within(this.el)){
50625             this.slideIn();
50626         }
50627     },
50628
50629     animateCollapse : function(){
50630         this.beforeSlide();
50631         this.el.setStyle("z-index", 20000);
50632         var anchor = this.getSlideAnchor();
50633         this.el.slideOut(anchor, {
50634             callback : function(){
50635                 this.el.setStyle("z-index", "");
50636                 this.collapsedEl.slideIn(anchor, {duration:.3});
50637                 this.afterSlide();
50638                 this.el.setLocation(-10000,-10000);
50639                 this.el.hide();
50640                 this.fireEvent("collapsed", this);
50641             },
50642             scope: this,
50643             block: true
50644         });
50645     },
50646
50647     animateExpand : function(){
50648         this.beforeSlide();
50649         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50650         this.el.setStyle("z-index", 20000);
50651         this.collapsedEl.hide({
50652             duration:.1
50653         });
50654         this.el.slideIn(this.getSlideAnchor(), {
50655             callback : function(){
50656                 this.el.setStyle("z-index", "");
50657                 this.afterSlide();
50658                 if(this.split){
50659                     this.split.el.show();
50660                 }
50661                 this.fireEvent("invalidated", this);
50662                 this.fireEvent("expanded", this);
50663             },
50664             scope: this,
50665             block: true
50666         });
50667     },
50668
50669     anchors : {
50670         "west" : "left",
50671         "east" : "right",
50672         "north" : "top",
50673         "south" : "bottom"
50674     },
50675
50676     sanchors : {
50677         "west" : "l",
50678         "east" : "r",
50679         "north" : "t",
50680         "south" : "b"
50681     },
50682
50683     canchors : {
50684         "west" : "tl-tr",
50685         "east" : "tr-tl",
50686         "north" : "tl-bl",
50687         "south" : "bl-tl"
50688     },
50689
50690     getAnchor : function(){
50691         return this.anchors[this.position];
50692     },
50693
50694     getCollapseAnchor : function(){
50695         return this.canchors[this.position];
50696     },
50697
50698     getSlideAnchor : function(){
50699         return this.sanchors[this.position];
50700     },
50701
50702     getAlignAdj : function(){
50703         var cm = this.cmargins;
50704         switch(this.position){
50705             case "west":
50706                 return [0, 0];
50707             break;
50708             case "east":
50709                 return [0, 0];
50710             break;
50711             case "north":
50712                 return [0, 0];
50713             break;
50714             case "south":
50715                 return [0, 0];
50716             break;
50717         }
50718     },
50719
50720     getExpandAdj : function(){
50721         var c = this.collapsedEl, cm = this.cmargins;
50722         switch(this.position){
50723             case "west":
50724                 return [-(cm.right+c.getWidth()+cm.left), 0];
50725             break;
50726             case "east":
50727                 return [cm.right+c.getWidth()+cm.left, 0];
50728             break;
50729             case "north":
50730                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50731             break;
50732             case "south":
50733                 return [0, cm.top+cm.bottom+c.getHeight()];
50734             break;
50735         }
50736     }
50737 });/*
50738  * Based on:
50739  * Ext JS Library 1.1.1
50740  * Copyright(c) 2006-2007, Ext JS, LLC.
50741  *
50742  * Originally Released Under LGPL - original licence link has changed is not relivant.
50743  *
50744  * Fork - LGPL
50745  * <script type="text/javascript">
50746  */
50747 /*
50748  * These classes are private internal classes
50749  */
50750 Roo.CenterLayoutRegion = function(mgr, config){
50751     Roo.LayoutRegion.call(this, mgr, config, "center");
50752     this.visible = true;
50753     this.minWidth = config.minWidth || 20;
50754     this.minHeight = config.minHeight || 20;
50755 };
50756
50757 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50758     hide : function(){
50759         // center panel can't be hidden
50760     },
50761     
50762     show : function(){
50763         // center panel can't be hidden
50764     },
50765     
50766     getMinWidth: function(){
50767         return this.minWidth;
50768     },
50769     
50770     getMinHeight: function(){
50771         return this.minHeight;
50772     }
50773 });
50774
50775
50776 Roo.NorthLayoutRegion = function(mgr, config){
50777     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50778     if(this.split){
50779         this.split.placement = Roo.SplitBar.TOP;
50780         this.split.orientation = Roo.SplitBar.VERTICAL;
50781         this.split.el.addClass("x-layout-split-v");
50782     }
50783     var size = config.initialSize || config.height;
50784     if(typeof size != "undefined"){
50785         this.el.setHeight(size);
50786     }
50787 };
50788 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50789     orientation: Roo.SplitBar.VERTICAL,
50790     getBox : function(){
50791         if(this.collapsed){
50792             return this.collapsedEl.getBox();
50793         }
50794         var box = this.el.getBox();
50795         if(this.split){
50796             box.height += this.split.el.getHeight();
50797         }
50798         return box;
50799     },
50800     
50801     updateBox : function(box){
50802         if(this.split && !this.collapsed){
50803             box.height -= this.split.el.getHeight();
50804             this.split.el.setLeft(box.x);
50805             this.split.el.setTop(box.y+box.height);
50806             this.split.el.setWidth(box.width);
50807         }
50808         if(this.collapsed){
50809             this.updateBody(box.width, null);
50810         }
50811         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50812     }
50813 });
50814
50815 Roo.SouthLayoutRegion = function(mgr, config){
50816     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50817     if(this.split){
50818         this.split.placement = Roo.SplitBar.BOTTOM;
50819         this.split.orientation = Roo.SplitBar.VERTICAL;
50820         this.split.el.addClass("x-layout-split-v");
50821     }
50822     var size = config.initialSize || config.height;
50823     if(typeof size != "undefined"){
50824         this.el.setHeight(size);
50825     }
50826 };
50827 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50828     orientation: Roo.SplitBar.VERTICAL,
50829     getBox : function(){
50830         if(this.collapsed){
50831             return this.collapsedEl.getBox();
50832         }
50833         var box = this.el.getBox();
50834         if(this.split){
50835             var sh = this.split.el.getHeight();
50836             box.height += sh;
50837             box.y -= sh;
50838         }
50839         return box;
50840     },
50841     
50842     updateBox : function(box){
50843         if(this.split && !this.collapsed){
50844             var sh = this.split.el.getHeight();
50845             box.height -= sh;
50846             box.y += sh;
50847             this.split.el.setLeft(box.x);
50848             this.split.el.setTop(box.y-sh);
50849             this.split.el.setWidth(box.width);
50850         }
50851         if(this.collapsed){
50852             this.updateBody(box.width, null);
50853         }
50854         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50855     }
50856 });
50857
50858 Roo.EastLayoutRegion = function(mgr, config){
50859     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50860     if(this.split){
50861         this.split.placement = Roo.SplitBar.RIGHT;
50862         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50863         this.split.el.addClass("x-layout-split-h");
50864     }
50865     var size = config.initialSize || config.width;
50866     if(typeof size != "undefined"){
50867         this.el.setWidth(size);
50868     }
50869 };
50870 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50871     orientation: Roo.SplitBar.HORIZONTAL,
50872     getBox : function(){
50873         if(this.collapsed){
50874             return this.collapsedEl.getBox();
50875         }
50876         var box = this.el.getBox();
50877         if(this.split){
50878             var sw = this.split.el.getWidth();
50879             box.width += sw;
50880             box.x -= sw;
50881         }
50882         return box;
50883     },
50884
50885     updateBox : function(box){
50886         if(this.split && !this.collapsed){
50887             var sw = this.split.el.getWidth();
50888             box.width -= sw;
50889             this.split.el.setLeft(box.x);
50890             this.split.el.setTop(box.y);
50891             this.split.el.setHeight(box.height);
50892             box.x += sw;
50893         }
50894         if(this.collapsed){
50895             this.updateBody(null, box.height);
50896         }
50897         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50898     }
50899 });
50900
50901 Roo.WestLayoutRegion = function(mgr, config){
50902     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50903     if(this.split){
50904         this.split.placement = Roo.SplitBar.LEFT;
50905         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50906         this.split.el.addClass("x-layout-split-h");
50907     }
50908     var size = config.initialSize || config.width;
50909     if(typeof size != "undefined"){
50910         this.el.setWidth(size);
50911     }
50912 };
50913 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50914     orientation: Roo.SplitBar.HORIZONTAL,
50915     getBox : function(){
50916         if(this.collapsed){
50917             return this.collapsedEl.getBox();
50918         }
50919         var box = this.el.getBox();
50920         if(this.split){
50921             box.width += this.split.el.getWidth();
50922         }
50923         return box;
50924     },
50925     
50926     updateBox : function(box){
50927         if(this.split && !this.collapsed){
50928             var sw = this.split.el.getWidth();
50929             box.width -= sw;
50930             this.split.el.setLeft(box.x+box.width);
50931             this.split.el.setTop(box.y);
50932             this.split.el.setHeight(box.height);
50933         }
50934         if(this.collapsed){
50935             this.updateBody(null, box.height);
50936         }
50937         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50938     }
50939 });
50940 /*
50941  * Based on:
50942  * Ext JS Library 1.1.1
50943  * Copyright(c) 2006-2007, Ext JS, LLC.
50944  *
50945  * Originally Released Under LGPL - original licence link has changed is not relivant.
50946  *
50947  * Fork - LGPL
50948  * <script type="text/javascript">
50949  */
50950  
50951  
50952 /*
50953  * Private internal class for reading and applying state
50954  */
50955 Roo.LayoutStateManager = function(layout){
50956      // default empty state
50957      this.state = {
50958         north: {},
50959         south: {},
50960         east: {},
50961         west: {}       
50962     };
50963 };
50964
50965 Roo.LayoutStateManager.prototype = {
50966     init : function(layout, provider){
50967         this.provider = provider;
50968         var state = provider.get(layout.id+"-layout-state");
50969         if(state){
50970             var wasUpdating = layout.isUpdating();
50971             if(!wasUpdating){
50972                 layout.beginUpdate();
50973             }
50974             for(var key in state){
50975                 if(typeof state[key] != "function"){
50976                     var rstate = state[key];
50977                     var r = layout.getRegion(key);
50978                     if(r && rstate){
50979                         if(rstate.size){
50980                             r.resizeTo(rstate.size);
50981                         }
50982                         if(rstate.collapsed == true){
50983                             r.collapse(true);
50984                         }else{
50985                             r.expand(null, true);
50986                         }
50987                     }
50988                 }
50989             }
50990             if(!wasUpdating){
50991                 layout.endUpdate();
50992             }
50993             this.state = state; 
50994         }
50995         this.layout = layout;
50996         layout.on("regionresized", this.onRegionResized, this);
50997         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50998         layout.on("regionexpanded", this.onRegionExpanded, this);
50999     },
51000     
51001     storeState : function(){
51002         this.provider.set(this.layout.id+"-layout-state", this.state);
51003     },
51004     
51005     onRegionResized : function(region, newSize){
51006         this.state[region.getPosition()].size = newSize;
51007         this.storeState();
51008     },
51009     
51010     onRegionCollapsed : function(region){
51011         this.state[region.getPosition()].collapsed = true;
51012         this.storeState();
51013     },
51014     
51015     onRegionExpanded : function(region){
51016         this.state[region.getPosition()].collapsed = false;
51017         this.storeState();
51018     }
51019 };/*
51020  * Based on:
51021  * Ext JS Library 1.1.1
51022  * Copyright(c) 2006-2007, Ext JS, LLC.
51023  *
51024  * Originally Released Under LGPL - original licence link has changed is not relivant.
51025  *
51026  * Fork - LGPL
51027  * <script type="text/javascript">
51028  */
51029 /**
51030  * @class Roo.ContentPanel
51031  * @extends Roo.util.Observable
51032  * A basic ContentPanel element.
51033  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51034  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51035  * @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
51036  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51037  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51038  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51039  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51040  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51041  * @cfg {String} title          The title for this panel
51042  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51043  * @cfg {String} url            Calls {@link #setUrl} with this value
51044  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51045  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51046  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51047  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51048
51049  * @constructor
51050  * Create a new ContentPanel.
51051  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51052  * @param {String/Object} config A string to set only the title or a config object
51053  * @param {String} content (optional) Set the HTML content for this panel
51054  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51055  */
51056 Roo.ContentPanel = function(el, config, content){
51057     
51058      
51059     /*
51060     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51061         config = el;
51062         el = Roo.id();
51063     }
51064     if (config && config.parentLayout) { 
51065         el = config.parentLayout.el.createChild(); 
51066     }
51067     */
51068     if(el.autoCreate){ // xtype is available if this is called from factory
51069         config = el;
51070         el = Roo.id();
51071     }
51072     this.el = Roo.get(el);
51073     if(!this.el && config && config.autoCreate){
51074         if(typeof config.autoCreate == "object"){
51075             if(!config.autoCreate.id){
51076                 config.autoCreate.id = config.id||el;
51077             }
51078             this.el = Roo.DomHelper.append(document.body,
51079                         config.autoCreate, true);
51080         }else{
51081             this.el = Roo.DomHelper.append(document.body,
51082                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51083         }
51084     }
51085     this.closable = false;
51086     this.loaded = false;
51087     this.active = false;
51088     if(typeof config == "string"){
51089         this.title = config;
51090     }else{
51091         Roo.apply(this, config);
51092     }
51093     
51094     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51095         this.wrapEl = this.el.wrap();
51096         this.toolbar.container = this.el.insertSibling(false, 'before');
51097         this.toolbar = new Roo.Toolbar(this.toolbar);
51098     }
51099     
51100     // xtype created footer. - not sure if will work as we normally have to render first..
51101     if (this.footer && !this.footer.el && this.footer.xtype) {
51102         if (!this.wrapEl) {
51103             this.wrapEl = this.el.wrap();
51104         }
51105     
51106         this.footer.container = this.wrapEl.createChild();
51107          
51108         this.footer = Roo.factory(this.footer, Roo);
51109         
51110     }
51111     
51112     if(this.resizeEl){
51113         this.resizeEl = Roo.get(this.resizeEl, true);
51114     }else{
51115         this.resizeEl = this.el;
51116     }
51117     // handle view.xtype
51118     
51119  
51120     
51121     
51122     this.addEvents({
51123         /**
51124          * @event activate
51125          * Fires when this panel is activated. 
51126          * @param {Roo.ContentPanel} this
51127          */
51128         "activate" : true,
51129         /**
51130          * @event deactivate
51131          * Fires when this panel is activated. 
51132          * @param {Roo.ContentPanel} this
51133          */
51134         "deactivate" : true,
51135
51136         /**
51137          * @event resize
51138          * Fires when this panel is resized if fitToFrame is true.
51139          * @param {Roo.ContentPanel} this
51140          * @param {Number} width The width after any component adjustments
51141          * @param {Number} height The height after any component adjustments
51142          */
51143         "resize" : true,
51144         
51145          /**
51146          * @event render
51147          * Fires when this tab is created
51148          * @param {Roo.ContentPanel} this
51149          */
51150         "render" : true
51151         
51152         
51153         
51154     });
51155     
51156
51157     
51158     
51159     if(this.autoScroll){
51160         this.resizeEl.setStyle("overflow", "auto");
51161     } else {
51162         // fix randome scrolling
51163         this.el.on('scroll', function() {
51164             Roo.log('fix random scolling');
51165             this.scrollTo('top',0); 
51166         });
51167     }
51168     content = content || this.content;
51169     if(content){
51170         this.setContent(content);
51171     }
51172     if(config && config.url){
51173         this.setUrl(this.url, this.params, this.loadOnce);
51174     }
51175     
51176     
51177     
51178     Roo.ContentPanel.superclass.constructor.call(this);
51179     
51180     if (this.view && typeof(this.view.xtype) != 'undefined') {
51181         this.view.el = this.el.appendChild(document.createElement("div"));
51182         this.view = Roo.factory(this.view); 
51183         this.view.render  &&  this.view.render(false, '');  
51184     }
51185     
51186     
51187     this.fireEvent('render', this);
51188 };
51189
51190 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51191     tabTip:'',
51192     setRegion : function(region){
51193         this.region = region;
51194         if(region){
51195            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51196         }else{
51197            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51198         } 
51199     },
51200     
51201     /**
51202      * Returns the toolbar for this Panel if one was configured. 
51203      * @return {Roo.Toolbar} 
51204      */
51205     getToolbar : function(){
51206         return this.toolbar;
51207     },
51208     
51209     setActiveState : function(active){
51210         this.active = active;
51211         if(!active){
51212             this.fireEvent("deactivate", this);
51213         }else{
51214             this.fireEvent("activate", this);
51215         }
51216     },
51217     /**
51218      * Updates this panel's element
51219      * @param {String} content The new content
51220      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51221     */
51222     setContent : function(content, loadScripts){
51223         this.el.update(content, loadScripts);
51224     },
51225
51226     ignoreResize : function(w, h){
51227         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51228             return true;
51229         }else{
51230             this.lastSize = {width: w, height: h};
51231             return false;
51232         }
51233     },
51234     /**
51235      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51236      * @return {Roo.UpdateManager} The UpdateManager
51237      */
51238     getUpdateManager : function(){
51239         return this.el.getUpdateManager();
51240     },
51241      /**
51242      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51243      * @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:
51244 <pre><code>
51245 panel.load({
51246     url: "your-url.php",
51247     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51248     callback: yourFunction,
51249     scope: yourObject, //(optional scope)
51250     discardUrl: false,
51251     nocache: false,
51252     text: "Loading...",
51253     timeout: 30,
51254     scripts: false
51255 });
51256 </code></pre>
51257      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51258      * 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.
51259      * @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}
51260      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51261      * @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.
51262      * @return {Roo.ContentPanel} this
51263      */
51264     load : function(){
51265         var um = this.el.getUpdateManager();
51266         um.update.apply(um, arguments);
51267         return this;
51268     },
51269
51270
51271     /**
51272      * 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.
51273      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51274      * @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)
51275      * @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)
51276      * @return {Roo.UpdateManager} The UpdateManager
51277      */
51278     setUrl : function(url, params, loadOnce){
51279         if(this.refreshDelegate){
51280             this.removeListener("activate", this.refreshDelegate);
51281         }
51282         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51283         this.on("activate", this.refreshDelegate);
51284         return this.el.getUpdateManager();
51285     },
51286     
51287     _handleRefresh : function(url, params, loadOnce){
51288         if(!loadOnce || !this.loaded){
51289             var updater = this.el.getUpdateManager();
51290             updater.update(url, params, this._setLoaded.createDelegate(this));
51291         }
51292     },
51293     
51294     _setLoaded : function(){
51295         this.loaded = true;
51296     }, 
51297     
51298     /**
51299      * Returns this panel's id
51300      * @return {String} 
51301      */
51302     getId : function(){
51303         return this.el.id;
51304     },
51305     
51306     /** 
51307      * Returns this panel's element - used by regiosn to add.
51308      * @return {Roo.Element} 
51309      */
51310     getEl : function(){
51311         return this.wrapEl || this.el;
51312     },
51313     
51314     adjustForComponents : function(width, height)
51315     {
51316         //Roo.log('adjustForComponents ');
51317         if(this.resizeEl != this.el){
51318             width -= this.el.getFrameWidth('lr');
51319             height -= this.el.getFrameWidth('tb');
51320         }
51321         if(this.toolbar){
51322             var te = this.toolbar.getEl();
51323             height -= te.getHeight();
51324             te.setWidth(width);
51325         }
51326         if(this.footer){
51327             var te = this.footer.getEl();
51328             Roo.log("footer:" + te.getHeight());
51329             
51330             height -= te.getHeight();
51331             te.setWidth(width);
51332         }
51333         
51334         
51335         if(this.adjustments){
51336             width += this.adjustments[0];
51337             height += this.adjustments[1];
51338         }
51339         return {"width": width, "height": height};
51340     },
51341     
51342     setSize : function(width, height){
51343         if(this.fitToFrame && !this.ignoreResize(width, height)){
51344             if(this.fitContainer && this.resizeEl != this.el){
51345                 this.el.setSize(width, height);
51346             }
51347             var size = this.adjustForComponents(width, height);
51348             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51349             this.fireEvent('resize', this, size.width, size.height);
51350         }
51351     },
51352     
51353     /**
51354      * Returns this panel's title
51355      * @return {String} 
51356      */
51357     getTitle : function(){
51358         return this.title;
51359     },
51360     
51361     /**
51362      * Set this panel's title
51363      * @param {String} title
51364      */
51365     setTitle : function(title){
51366         this.title = title;
51367         if(this.region){
51368             this.region.updatePanelTitle(this, title);
51369         }
51370     },
51371     
51372     /**
51373      * Returns true is this panel was configured to be closable
51374      * @return {Boolean} 
51375      */
51376     isClosable : function(){
51377         return this.closable;
51378     },
51379     
51380     beforeSlide : function(){
51381         this.el.clip();
51382         this.resizeEl.clip();
51383     },
51384     
51385     afterSlide : function(){
51386         this.el.unclip();
51387         this.resizeEl.unclip();
51388     },
51389     
51390     /**
51391      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51392      *   Will fail silently if the {@link #setUrl} method has not been called.
51393      *   This does not activate the panel, just updates its content.
51394      */
51395     refresh : function(){
51396         if(this.refreshDelegate){
51397            this.loaded = false;
51398            this.refreshDelegate();
51399         }
51400     },
51401     
51402     /**
51403      * Destroys this panel
51404      */
51405     destroy : function(){
51406         this.el.removeAllListeners();
51407         var tempEl = document.createElement("span");
51408         tempEl.appendChild(this.el.dom);
51409         tempEl.innerHTML = "";
51410         this.el.remove();
51411         this.el = null;
51412     },
51413     
51414     /**
51415      * form - if the content panel contains a form - this is a reference to it.
51416      * @type {Roo.form.Form}
51417      */
51418     form : false,
51419     /**
51420      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51421      *    This contains a reference to it.
51422      * @type {Roo.View}
51423      */
51424     view : false,
51425     
51426       /**
51427      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51428      * <pre><code>
51429
51430 layout.addxtype({
51431        xtype : 'Form',
51432        items: [ .... ]
51433    }
51434 );
51435
51436 </code></pre>
51437      * @param {Object} cfg Xtype definition of item to add.
51438      */
51439     
51440     addxtype : function(cfg) {
51441         // add form..
51442         if (cfg.xtype.match(/^Form$/)) {
51443             
51444             var el;
51445             //if (this.footer) {
51446             //    el = this.footer.container.insertSibling(false, 'before');
51447             //} else {
51448                 el = this.el.createChild();
51449             //}
51450
51451             this.form = new  Roo.form.Form(cfg);
51452             
51453             
51454             if ( this.form.allItems.length) this.form.render(el.dom);
51455             return this.form;
51456         }
51457         // should only have one of theses..
51458         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51459             // views.. should not be just added - used named prop 'view''
51460             
51461             cfg.el = this.el.appendChild(document.createElement("div"));
51462             // factory?
51463             
51464             var ret = new Roo.factory(cfg);
51465              
51466              ret.render && ret.render(false, ''); // render blank..
51467             this.view = ret;
51468             return ret;
51469         }
51470         return false;
51471     }
51472 });
51473
51474 /**
51475  * @class Roo.GridPanel
51476  * @extends Roo.ContentPanel
51477  * @constructor
51478  * Create a new GridPanel.
51479  * @param {Roo.grid.Grid} grid The grid for this panel
51480  * @param {String/Object} config A string to set only the panel's title, or a config object
51481  */
51482 Roo.GridPanel = function(grid, config){
51483     
51484   
51485     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51486         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51487         
51488     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51489     
51490     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51491     
51492     if(this.toolbar){
51493         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51494     }
51495     // xtype created footer. - not sure if will work as we normally have to render first..
51496     if (this.footer && !this.footer.el && this.footer.xtype) {
51497         
51498         this.footer.container = this.grid.getView().getFooterPanel(true);
51499         this.footer.dataSource = this.grid.dataSource;
51500         this.footer = Roo.factory(this.footer, Roo);
51501         
51502     }
51503     
51504     grid.monitorWindowResize = false; // turn off autosizing
51505     grid.autoHeight = false;
51506     grid.autoWidth = false;
51507     this.grid = grid;
51508     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51509 };
51510
51511 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51512     getId : function(){
51513         return this.grid.id;
51514     },
51515     
51516     /**
51517      * Returns the grid for this panel
51518      * @return {Roo.grid.Grid} 
51519      */
51520     getGrid : function(){
51521         return this.grid;    
51522     },
51523     
51524     setSize : function(width, height){
51525         if(!this.ignoreResize(width, height)){
51526             var grid = this.grid;
51527             var size = this.adjustForComponents(width, height);
51528             grid.getGridEl().setSize(size.width, size.height);
51529             grid.autoSize();
51530         }
51531     },
51532     
51533     beforeSlide : function(){
51534         this.grid.getView().scroller.clip();
51535     },
51536     
51537     afterSlide : function(){
51538         this.grid.getView().scroller.unclip();
51539     },
51540     
51541     destroy : function(){
51542         this.grid.destroy();
51543         delete this.grid;
51544         Roo.GridPanel.superclass.destroy.call(this); 
51545     }
51546 });
51547
51548
51549 /**
51550  * @class Roo.NestedLayoutPanel
51551  * @extends Roo.ContentPanel
51552  * @constructor
51553  * Create a new NestedLayoutPanel.
51554  * 
51555  * 
51556  * @param {Roo.BorderLayout} layout The layout for this panel
51557  * @param {String/Object} config A string to set only the title or a config object
51558  */
51559 Roo.NestedLayoutPanel = function(layout, config)
51560 {
51561     // construct with only one argument..
51562     /* FIXME - implement nicer consturctors
51563     if (layout.layout) {
51564         config = layout;
51565         layout = config.layout;
51566         delete config.layout;
51567     }
51568     if (layout.xtype && !layout.getEl) {
51569         // then layout needs constructing..
51570         layout = Roo.factory(layout, Roo);
51571     }
51572     */
51573     
51574     
51575     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51576     
51577     layout.monitorWindowResize = false; // turn off autosizing
51578     this.layout = layout;
51579     this.layout.getEl().addClass("x-layout-nested-layout");
51580     
51581     
51582     
51583     
51584 };
51585
51586 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51587
51588     setSize : function(width, height){
51589         if(!this.ignoreResize(width, height)){
51590             var size = this.adjustForComponents(width, height);
51591             var el = this.layout.getEl();
51592             el.setSize(size.width, size.height);
51593             var touch = el.dom.offsetWidth;
51594             this.layout.layout();
51595             // ie requires a double layout on the first pass
51596             if(Roo.isIE && !this.initialized){
51597                 this.initialized = true;
51598                 this.layout.layout();
51599             }
51600         }
51601     },
51602     
51603     // activate all subpanels if not currently active..
51604     
51605     setActiveState : function(active){
51606         this.active = active;
51607         if(!active){
51608             this.fireEvent("deactivate", this);
51609             return;
51610         }
51611         
51612         this.fireEvent("activate", this);
51613         // not sure if this should happen before or after..
51614         if (!this.layout) {
51615             return; // should not happen..
51616         }
51617         var reg = false;
51618         for (var r in this.layout.regions) {
51619             reg = this.layout.getRegion(r);
51620             if (reg.getActivePanel()) {
51621                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51622                 reg.setActivePanel(reg.getActivePanel());
51623                 continue;
51624             }
51625             if (!reg.panels.length) {
51626                 continue;
51627             }
51628             reg.showPanel(reg.getPanel(0));
51629         }
51630         
51631         
51632         
51633         
51634     },
51635     
51636     /**
51637      * Returns the nested BorderLayout for this panel
51638      * @return {Roo.BorderLayout} 
51639      */
51640     getLayout : function(){
51641         return this.layout;
51642     },
51643     
51644      /**
51645      * Adds a xtype elements to the layout of the nested panel
51646      * <pre><code>
51647
51648 panel.addxtype({
51649        xtype : 'ContentPanel',
51650        region: 'west',
51651        items: [ .... ]
51652    }
51653 );
51654
51655 panel.addxtype({
51656         xtype : 'NestedLayoutPanel',
51657         region: 'west',
51658         layout: {
51659            center: { },
51660            west: { }   
51661         },
51662         items : [ ... list of content panels or nested layout panels.. ]
51663    }
51664 );
51665 </code></pre>
51666      * @param {Object} cfg Xtype definition of item to add.
51667      */
51668     addxtype : function(cfg) {
51669         return this.layout.addxtype(cfg);
51670     
51671     }
51672 });
51673
51674 Roo.ScrollPanel = function(el, config, content){
51675     config = config || {};
51676     config.fitToFrame = true;
51677     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51678     
51679     this.el.dom.style.overflow = "hidden";
51680     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51681     this.el.removeClass("x-layout-inactive-content");
51682     this.el.on("mousewheel", this.onWheel, this);
51683
51684     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51685     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51686     up.unselectable(); down.unselectable();
51687     up.on("click", this.scrollUp, this);
51688     down.on("click", this.scrollDown, this);
51689     up.addClassOnOver("x-scroller-btn-over");
51690     down.addClassOnOver("x-scroller-btn-over");
51691     up.addClassOnClick("x-scroller-btn-click");
51692     down.addClassOnClick("x-scroller-btn-click");
51693     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51694
51695     this.resizeEl = this.el;
51696     this.el = wrap; this.up = up; this.down = down;
51697 };
51698
51699 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51700     increment : 100,
51701     wheelIncrement : 5,
51702     scrollUp : function(){
51703         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51704     },
51705
51706     scrollDown : function(){
51707         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51708     },
51709
51710     afterScroll : function(){
51711         var el = this.resizeEl;
51712         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51713         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51714         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51715     },
51716
51717     setSize : function(){
51718         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51719         this.afterScroll();
51720     },
51721
51722     onWheel : function(e){
51723         var d = e.getWheelDelta();
51724         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51725         this.afterScroll();
51726         e.stopEvent();
51727     },
51728
51729     setContent : function(content, loadScripts){
51730         this.resizeEl.update(content, loadScripts);
51731     }
51732
51733 });
51734
51735
51736
51737
51738
51739
51740
51741
51742
51743 /**
51744  * @class Roo.TreePanel
51745  * @extends Roo.ContentPanel
51746  * @constructor
51747  * Create a new TreePanel. - defaults to fit/scoll contents.
51748  * @param {String/Object} config A string to set only the panel's title, or a config object
51749  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51750  */
51751 Roo.TreePanel = function(config){
51752     var el = config.el;
51753     var tree = config.tree;
51754     delete config.tree; 
51755     delete config.el; // hopefull!
51756     
51757     // wrapper for IE7 strict & safari scroll issue
51758     
51759     var treeEl = el.createChild();
51760     config.resizeEl = treeEl;
51761     
51762     
51763     
51764     Roo.TreePanel.superclass.constructor.call(this, el, config);
51765  
51766  
51767     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51768     //console.log(tree);
51769     this.on('activate', function()
51770     {
51771         if (this.tree.rendered) {
51772             return;
51773         }
51774         //console.log('render tree');
51775         this.tree.render();
51776     });
51777     // this should not be needed.. - it's actually the 'el' that resizes?
51778     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51779     
51780     //this.on('resize',  function (cp, w, h) {
51781     //        this.tree.innerCt.setWidth(w);
51782     //        this.tree.innerCt.setHeight(h);
51783     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51784     //});
51785
51786         
51787     
51788 };
51789
51790 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51791     fitToFrame : true,
51792     autoScroll : true
51793 });
51794
51795
51796
51797
51798
51799
51800
51801
51802
51803
51804
51805 /*
51806  * Based on:
51807  * Ext JS Library 1.1.1
51808  * Copyright(c) 2006-2007, Ext JS, LLC.
51809  *
51810  * Originally Released Under LGPL - original licence link has changed is not relivant.
51811  *
51812  * Fork - LGPL
51813  * <script type="text/javascript">
51814  */
51815  
51816
51817 /**
51818  * @class Roo.ReaderLayout
51819  * @extends Roo.BorderLayout
51820  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51821  * center region containing two nested regions (a top one for a list view and one for item preview below),
51822  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51823  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51824  * expedites the setup of the overall layout and regions for this common application style.
51825  * Example:
51826  <pre><code>
51827 var reader = new Roo.ReaderLayout();
51828 var CP = Roo.ContentPanel;  // shortcut for adding
51829
51830 reader.beginUpdate();
51831 reader.add("north", new CP("north", "North"));
51832 reader.add("west", new CP("west", {title: "West"}));
51833 reader.add("east", new CP("east", {title: "East"}));
51834
51835 reader.regions.listView.add(new CP("listView", "List"));
51836 reader.regions.preview.add(new CP("preview", "Preview"));
51837 reader.endUpdate();
51838 </code></pre>
51839 * @constructor
51840 * Create a new ReaderLayout
51841 * @param {Object} config Configuration options
51842 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51843 * document.body if omitted)
51844 */
51845 Roo.ReaderLayout = function(config, renderTo){
51846     var c = config || {size:{}};
51847     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51848         north: c.north !== false ? Roo.apply({
51849             split:false,
51850             initialSize: 32,
51851             titlebar: false
51852         }, c.north) : false,
51853         west: c.west !== false ? Roo.apply({
51854             split:true,
51855             initialSize: 200,
51856             minSize: 175,
51857             maxSize: 400,
51858             titlebar: true,
51859             collapsible: true,
51860             animate: true,
51861             margins:{left:5,right:0,bottom:5,top:5},
51862             cmargins:{left:5,right:5,bottom:5,top:5}
51863         }, c.west) : false,
51864         east: c.east !== false ? Roo.apply({
51865             split:true,
51866             initialSize: 200,
51867             minSize: 175,
51868             maxSize: 400,
51869             titlebar: true,
51870             collapsible: true,
51871             animate: true,
51872             margins:{left:0,right:5,bottom:5,top:5},
51873             cmargins:{left:5,right:5,bottom:5,top:5}
51874         }, c.east) : false,
51875         center: Roo.apply({
51876             tabPosition: 'top',
51877             autoScroll:false,
51878             closeOnTab: true,
51879             titlebar:false,
51880             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51881         }, c.center)
51882     });
51883
51884     this.el.addClass('x-reader');
51885
51886     this.beginUpdate();
51887
51888     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51889         south: c.preview !== false ? Roo.apply({
51890             split:true,
51891             initialSize: 200,
51892             minSize: 100,
51893             autoScroll:true,
51894             collapsible:true,
51895             titlebar: true,
51896             cmargins:{top:5,left:0, right:0, bottom:0}
51897         }, c.preview) : false,
51898         center: Roo.apply({
51899             autoScroll:false,
51900             titlebar:false,
51901             minHeight:200
51902         }, c.listView)
51903     });
51904     this.add('center', new Roo.NestedLayoutPanel(inner,
51905             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51906
51907     this.endUpdate();
51908
51909     this.regions.preview = inner.getRegion('south');
51910     this.regions.listView = inner.getRegion('center');
51911 };
51912
51913 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51914  * Based on:
51915  * Ext JS Library 1.1.1
51916  * Copyright(c) 2006-2007, Ext JS, LLC.
51917  *
51918  * Originally Released Under LGPL - original licence link has changed is not relivant.
51919  *
51920  * Fork - LGPL
51921  * <script type="text/javascript">
51922  */
51923  
51924 /**
51925  * @class Roo.grid.Grid
51926  * @extends Roo.util.Observable
51927  * This class represents the primary interface of a component based grid control.
51928  * <br><br>Usage:<pre><code>
51929  var grid = new Roo.grid.Grid("my-container-id", {
51930      ds: myDataStore,
51931      cm: myColModel,
51932      selModel: mySelectionModel,
51933      autoSizeColumns: true,
51934      monitorWindowResize: false,
51935      trackMouseOver: true
51936  });
51937  // set any options
51938  grid.render();
51939  * </code></pre>
51940  * <b>Common Problems:</b><br/>
51941  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51942  * element will correct this<br/>
51943  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51944  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51945  * are unpredictable.<br/>
51946  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51947  * grid to calculate dimensions/offsets.<br/>
51948   * @constructor
51949  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51950  * The container MUST have some type of size defined for the grid to fill. The container will be
51951  * automatically set to position relative if it isn't already.
51952  * @param {Object} config A config object that sets properties on this grid.
51953  */
51954 Roo.grid.Grid = function(container, config){
51955         // initialize the container
51956         this.container = Roo.get(container);
51957         this.container.update("");
51958         this.container.setStyle("overflow", "hidden");
51959     this.container.addClass('x-grid-container');
51960
51961     this.id = this.container.id;
51962
51963     Roo.apply(this, config);
51964     // check and correct shorthanded configs
51965     if(this.ds){
51966         this.dataSource = this.ds;
51967         delete this.ds;
51968     }
51969     if(this.cm){
51970         this.colModel = this.cm;
51971         delete this.cm;
51972     }
51973     if(this.sm){
51974         this.selModel = this.sm;
51975         delete this.sm;
51976     }
51977
51978     if (this.selModel) {
51979         this.selModel = Roo.factory(this.selModel, Roo.grid);
51980         this.sm = this.selModel;
51981         this.sm.xmodule = this.xmodule || false;
51982     }
51983     if (typeof(this.colModel.config) == 'undefined') {
51984         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51985         this.cm = this.colModel;
51986         this.cm.xmodule = this.xmodule || false;
51987     }
51988     if (this.dataSource) {
51989         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51990         this.ds = this.dataSource;
51991         this.ds.xmodule = this.xmodule || false;
51992          
51993     }
51994     
51995     
51996     
51997     if(this.width){
51998         this.container.setWidth(this.width);
51999     }
52000
52001     if(this.height){
52002         this.container.setHeight(this.height);
52003     }
52004     /** @private */
52005         this.addEvents({
52006         // raw events
52007         /**
52008          * @event click
52009          * The raw click event for the entire grid.
52010          * @param {Roo.EventObject} e
52011          */
52012         "click" : true,
52013         /**
52014          * @event dblclick
52015          * The raw dblclick event for the entire grid.
52016          * @param {Roo.EventObject} e
52017          */
52018         "dblclick" : true,
52019         /**
52020          * @event contextmenu
52021          * The raw contextmenu event for the entire grid.
52022          * @param {Roo.EventObject} e
52023          */
52024         "contextmenu" : true,
52025         /**
52026          * @event mousedown
52027          * The raw mousedown event for the entire grid.
52028          * @param {Roo.EventObject} e
52029          */
52030         "mousedown" : true,
52031         /**
52032          * @event mouseup
52033          * The raw mouseup event for the entire grid.
52034          * @param {Roo.EventObject} e
52035          */
52036         "mouseup" : true,
52037         /**
52038          * @event mouseover
52039          * The raw mouseover event for the entire grid.
52040          * @param {Roo.EventObject} e
52041          */
52042         "mouseover" : true,
52043         /**
52044          * @event mouseout
52045          * The raw mouseout event for the entire grid.
52046          * @param {Roo.EventObject} e
52047          */
52048         "mouseout" : true,
52049         /**
52050          * @event keypress
52051          * The raw keypress event for the entire grid.
52052          * @param {Roo.EventObject} e
52053          */
52054         "keypress" : true,
52055         /**
52056          * @event keydown
52057          * The raw keydown event for the entire grid.
52058          * @param {Roo.EventObject} e
52059          */
52060         "keydown" : true,
52061
52062         // custom events
52063
52064         /**
52065          * @event cellclick
52066          * Fires when a cell is clicked
52067          * @param {Grid} this
52068          * @param {Number} rowIndex
52069          * @param {Number} columnIndex
52070          * @param {Roo.EventObject} e
52071          */
52072         "cellclick" : true,
52073         /**
52074          * @event celldblclick
52075          * Fires when a cell is double clicked
52076          * @param {Grid} this
52077          * @param {Number} rowIndex
52078          * @param {Number} columnIndex
52079          * @param {Roo.EventObject} e
52080          */
52081         "celldblclick" : true,
52082         /**
52083          * @event rowclick
52084          * Fires when a row is clicked
52085          * @param {Grid} this
52086          * @param {Number} rowIndex
52087          * @param {Roo.EventObject} e
52088          */
52089         "rowclick" : true,
52090         /**
52091          * @event rowdblclick
52092          * Fires when a row is double clicked
52093          * @param {Grid} this
52094          * @param {Number} rowIndex
52095          * @param {Roo.EventObject} e
52096          */
52097         "rowdblclick" : true,
52098         /**
52099          * @event headerclick
52100          * Fires when a header is clicked
52101          * @param {Grid} this
52102          * @param {Number} columnIndex
52103          * @param {Roo.EventObject} e
52104          */
52105         "headerclick" : true,
52106         /**
52107          * @event headerdblclick
52108          * Fires when a header cell is double clicked
52109          * @param {Grid} this
52110          * @param {Number} columnIndex
52111          * @param {Roo.EventObject} e
52112          */
52113         "headerdblclick" : true,
52114         /**
52115          * @event rowcontextmenu
52116          * Fires when a row is right clicked
52117          * @param {Grid} this
52118          * @param {Number} rowIndex
52119          * @param {Roo.EventObject} e
52120          */
52121         "rowcontextmenu" : true,
52122         /**
52123          * @event cellcontextmenu
52124          * Fires when a cell is right clicked
52125          * @param {Grid} this
52126          * @param {Number} rowIndex
52127          * @param {Number} cellIndex
52128          * @param {Roo.EventObject} e
52129          */
52130          "cellcontextmenu" : true,
52131         /**
52132          * @event headercontextmenu
52133          * Fires when a header is right clicked
52134          * @param {Grid} this
52135          * @param {Number} columnIndex
52136          * @param {Roo.EventObject} e
52137          */
52138         "headercontextmenu" : true,
52139         /**
52140          * @event bodyscroll
52141          * Fires when the body element is scrolled
52142          * @param {Number} scrollLeft
52143          * @param {Number} scrollTop
52144          */
52145         "bodyscroll" : true,
52146         /**
52147          * @event columnresize
52148          * Fires when the user resizes a column
52149          * @param {Number} columnIndex
52150          * @param {Number} newSize
52151          */
52152         "columnresize" : true,
52153         /**
52154          * @event columnmove
52155          * Fires when the user moves a column
52156          * @param {Number} oldIndex
52157          * @param {Number} newIndex
52158          */
52159         "columnmove" : true,
52160         /**
52161          * @event startdrag
52162          * Fires when row(s) start being dragged
52163          * @param {Grid} this
52164          * @param {Roo.GridDD} dd The drag drop object
52165          * @param {event} e The raw browser event
52166          */
52167         "startdrag" : true,
52168         /**
52169          * @event enddrag
52170          * Fires when a drag operation is complete
52171          * @param {Grid} this
52172          * @param {Roo.GridDD} dd The drag drop object
52173          * @param {event} e The raw browser event
52174          */
52175         "enddrag" : true,
52176         /**
52177          * @event dragdrop
52178          * Fires when dragged row(s) are dropped on a valid DD target
52179          * @param {Grid} this
52180          * @param {Roo.GridDD} dd The drag drop object
52181          * @param {String} targetId The target drag drop object
52182          * @param {event} e The raw browser event
52183          */
52184         "dragdrop" : true,
52185         /**
52186          * @event dragover
52187          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52188          * @param {Grid} this
52189          * @param {Roo.GridDD} dd The drag drop object
52190          * @param {String} targetId The target drag drop object
52191          * @param {event} e The raw browser event
52192          */
52193         "dragover" : true,
52194         /**
52195          * @event dragenter
52196          *  Fires when the dragged row(s) first cross another DD target while being dragged
52197          * @param {Grid} this
52198          * @param {Roo.GridDD} dd The drag drop object
52199          * @param {String} targetId The target drag drop object
52200          * @param {event} e The raw browser event
52201          */
52202         "dragenter" : true,
52203         /**
52204          * @event dragout
52205          * Fires when the dragged row(s) leave another DD target while being dragged
52206          * @param {Grid} this
52207          * @param {Roo.GridDD} dd The drag drop object
52208          * @param {String} targetId The target drag drop object
52209          * @param {event} e The raw browser event
52210          */
52211         "dragout" : true,
52212         /**
52213          * @event rowclass
52214          * Fires when a row is rendered, so you can change add a style to it.
52215          * @param {GridView} gridview   The grid view
52216          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52217          */
52218         'rowclass' : true,
52219
52220         /**
52221          * @event render
52222          * Fires when the grid is rendered
52223          * @param {Grid} grid
52224          */
52225         'render' : true
52226     });
52227
52228     Roo.grid.Grid.superclass.constructor.call(this);
52229 };
52230 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52231     
52232     /**
52233      * @cfg {String} ddGroup - drag drop group.
52234      */
52235
52236     /**
52237      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52238      */
52239     minColumnWidth : 25,
52240
52241     /**
52242      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52243      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52244      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52245      */
52246     autoSizeColumns : false,
52247
52248     /**
52249      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52250      */
52251     autoSizeHeaders : true,
52252
52253     /**
52254      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52255      */
52256     monitorWindowResize : true,
52257
52258     /**
52259      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52260      * rows measured to get a columns size. Default is 0 (all rows).
52261      */
52262     maxRowsToMeasure : 0,
52263
52264     /**
52265      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52266      */
52267     trackMouseOver : true,
52268
52269     /**
52270     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52271     */
52272     
52273     /**
52274     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52275     */
52276     enableDragDrop : false,
52277     
52278     /**
52279     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52280     */
52281     enableColumnMove : true,
52282     
52283     /**
52284     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52285     */
52286     enableColumnHide : true,
52287     
52288     /**
52289     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52290     */
52291     enableRowHeightSync : false,
52292     
52293     /**
52294     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52295     */
52296     stripeRows : true,
52297     
52298     /**
52299     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52300     */
52301     autoHeight : false,
52302
52303     /**
52304      * @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.
52305      */
52306     autoExpandColumn : false,
52307
52308     /**
52309     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52310     * Default is 50.
52311     */
52312     autoExpandMin : 50,
52313
52314     /**
52315     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52316     */
52317     autoExpandMax : 1000,
52318
52319     /**
52320     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52321     */
52322     view : null,
52323
52324     /**
52325     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52326     */
52327     loadMask : false,
52328     /**
52329     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52330     */
52331     dropTarget: false,
52332     
52333    
52334     
52335     // private
52336     rendered : false,
52337
52338     /**
52339     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52340     * of a fixed width. Default is false.
52341     */
52342     /**
52343     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52344     */
52345     /**
52346      * Called once after all setup has been completed and the grid is ready to be rendered.
52347      * @return {Roo.grid.Grid} this
52348      */
52349     render : function()
52350     {
52351         var c = this.container;
52352         // try to detect autoHeight/width mode
52353         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52354             this.autoHeight = true;
52355         }
52356         var view = this.getView();
52357         view.init(this);
52358
52359         c.on("click", this.onClick, this);
52360         c.on("dblclick", this.onDblClick, this);
52361         c.on("contextmenu", this.onContextMenu, this);
52362         c.on("keydown", this.onKeyDown, this);
52363         if (Roo.isTouch) {
52364             c.on("touchstart", this.onTouchStart, this);
52365         }
52366
52367         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52368
52369         this.getSelectionModel().init(this);
52370
52371         view.render();
52372
52373         if(this.loadMask){
52374             this.loadMask = new Roo.LoadMask(this.container,
52375                     Roo.apply({store:this.dataSource}, this.loadMask));
52376         }
52377         
52378         
52379         if (this.toolbar && this.toolbar.xtype) {
52380             this.toolbar.container = this.getView().getHeaderPanel(true);
52381             this.toolbar = new Roo.Toolbar(this.toolbar);
52382         }
52383         if (this.footer && this.footer.xtype) {
52384             this.footer.dataSource = this.getDataSource();
52385             this.footer.container = this.getView().getFooterPanel(true);
52386             this.footer = Roo.factory(this.footer, Roo);
52387         }
52388         if (this.dropTarget && this.dropTarget.xtype) {
52389             delete this.dropTarget.xtype;
52390             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52391         }
52392         
52393         
52394         this.rendered = true;
52395         this.fireEvent('render', this);
52396         return this;
52397     },
52398
52399         /**
52400          * Reconfigures the grid to use a different Store and Column Model.
52401          * The View will be bound to the new objects and refreshed.
52402          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52403          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52404          */
52405     reconfigure : function(dataSource, colModel){
52406         if(this.loadMask){
52407             this.loadMask.destroy();
52408             this.loadMask = new Roo.LoadMask(this.container,
52409                     Roo.apply({store:dataSource}, this.loadMask));
52410         }
52411         this.view.bind(dataSource, colModel);
52412         this.dataSource = dataSource;
52413         this.colModel = colModel;
52414         this.view.refresh(true);
52415     },
52416
52417     // private
52418     onKeyDown : function(e){
52419         this.fireEvent("keydown", e);
52420     },
52421
52422     /**
52423      * Destroy this grid.
52424      * @param {Boolean} removeEl True to remove the element
52425      */
52426     destroy : function(removeEl, keepListeners){
52427         if(this.loadMask){
52428             this.loadMask.destroy();
52429         }
52430         var c = this.container;
52431         c.removeAllListeners();
52432         this.view.destroy();
52433         this.colModel.purgeListeners();
52434         if(!keepListeners){
52435             this.purgeListeners();
52436         }
52437         c.update("");
52438         if(removeEl === true){
52439             c.remove();
52440         }
52441     },
52442
52443     // private
52444     processEvent : function(name, e){
52445         // does this fire select???
52446         Roo.log('grid:processEvent '  + name);
52447         
52448         if (name != 'touchstart' ) {
52449             this.fireEvent(name, e);    
52450         }
52451         
52452         var t = e.getTarget();
52453         var v = this.view;
52454         var header = v.findHeaderIndex(t);
52455         if(header !== false){
52456             var ename = name == 'touchstart' ? 'click' : name;
52457              
52458             this.fireEvent("header" + ename, this, header, e);
52459         }else{
52460             var row = v.findRowIndex(t);
52461             var cell = v.findCellIndex(t);
52462             if (name == 'touchstart') {
52463                 // first touch is always a click.
52464                 // hopefull this happens after selection is updated.?
52465                 name = false;
52466                 
52467                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52468                     var cs = this.selModel.getSelectedCell();
52469                     if (row == cs[0] && cell == cs[1]){
52470                         name = 'dblclick';
52471                     }
52472                 }
52473                 if (typeof(this.selModel.getSelections) != 'undefined') {
52474                     var cs = this.selModel.getSelections();
52475                     var ds = this.dataSource;
52476                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52477                         name = 'dblclick';
52478                     }
52479                 }
52480                 if (!name) {
52481                     return;
52482                 }
52483             }
52484             
52485             
52486             if(row !== false){
52487                 this.fireEvent("row" + name, this, row, e);
52488                 if(cell !== false){
52489                     this.fireEvent("cell" + name, this, row, cell, e);
52490                 }
52491             }
52492         }
52493     },
52494
52495     // private
52496     onClick : function(e){
52497         this.processEvent("click", e);
52498     },
52499    // private
52500     onTouchStart : function(e){
52501         this.processEvent("touchstart", e);
52502     },
52503
52504     // private
52505     onContextMenu : function(e, t){
52506         this.processEvent("contextmenu", e);
52507     },
52508
52509     // private
52510     onDblClick : function(e){
52511         this.processEvent("dblclick", e);
52512     },
52513
52514     // private
52515     walkCells : function(row, col, step, fn, scope){
52516         var cm = this.colModel, clen = cm.getColumnCount();
52517         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52518         if(step < 0){
52519             if(col < 0){
52520                 row--;
52521                 first = false;
52522             }
52523             while(row >= 0){
52524                 if(!first){
52525                     col = clen-1;
52526                 }
52527                 first = false;
52528                 while(col >= 0){
52529                     if(fn.call(scope || this, row, col, cm) === true){
52530                         return [row, col];
52531                     }
52532                     col--;
52533                 }
52534                 row--;
52535             }
52536         } else {
52537             if(col >= clen){
52538                 row++;
52539                 first = false;
52540             }
52541             while(row < rlen){
52542                 if(!first){
52543                     col = 0;
52544                 }
52545                 first = false;
52546                 while(col < clen){
52547                     if(fn.call(scope || this, row, col, cm) === true){
52548                         return [row, col];
52549                     }
52550                     col++;
52551                 }
52552                 row++;
52553             }
52554         }
52555         return null;
52556     },
52557
52558     // private
52559     getSelections : function(){
52560         return this.selModel.getSelections();
52561     },
52562
52563     /**
52564      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52565      * but if manual update is required this method will initiate it.
52566      */
52567     autoSize : function(){
52568         if(this.rendered){
52569             this.view.layout();
52570             if(this.view.adjustForScroll){
52571                 this.view.adjustForScroll();
52572             }
52573         }
52574     },
52575
52576     /**
52577      * Returns the grid's underlying element.
52578      * @return {Element} The element
52579      */
52580     getGridEl : function(){
52581         return this.container;
52582     },
52583
52584     // private for compatibility, overridden by editor grid
52585     stopEditing : function(){},
52586
52587     /**
52588      * Returns the grid's SelectionModel.
52589      * @return {SelectionModel}
52590      */
52591     getSelectionModel : function(){
52592         if(!this.selModel){
52593             this.selModel = new Roo.grid.RowSelectionModel();
52594         }
52595         return this.selModel;
52596     },
52597
52598     /**
52599      * Returns the grid's DataSource.
52600      * @return {DataSource}
52601      */
52602     getDataSource : function(){
52603         return this.dataSource;
52604     },
52605
52606     /**
52607      * Returns the grid's ColumnModel.
52608      * @return {ColumnModel}
52609      */
52610     getColumnModel : function(){
52611         return this.colModel;
52612     },
52613
52614     /**
52615      * Returns the grid's GridView object.
52616      * @return {GridView}
52617      */
52618     getView : function(){
52619         if(!this.view){
52620             this.view = new Roo.grid.GridView(this.viewConfig);
52621         }
52622         return this.view;
52623     },
52624     /**
52625      * Called to get grid's drag proxy text, by default returns this.ddText.
52626      * @return {String}
52627      */
52628     getDragDropText : function(){
52629         var count = this.selModel.getCount();
52630         return String.format(this.ddText, count, count == 1 ? '' : 's');
52631     }
52632 });
52633 /**
52634  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52635  * %0 is replaced with the number of selected rows.
52636  * @type String
52637  */
52638 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52639  * Based on:
52640  * Ext JS Library 1.1.1
52641  * Copyright(c) 2006-2007, Ext JS, LLC.
52642  *
52643  * Originally Released Under LGPL - original licence link has changed is not relivant.
52644  *
52645  * Fork - LGPL
52646  * <script type="text/javascript">
52647  */
52648  
52649 Roo.grid.AbstractGridView = function(){
52650         this.grid = null;
52651         
52652         this.events = {
52653             "beforerowremoved" : true,
52654             "beforerowsinserted" : true,
52655             "beforerefresh" : true,
52656             "rowremoved" : true,
52657             "rowsinserted" : true,
52658             "rowupdated" : true,
52659             "refresh" : true
52660         };
52661     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52662 };
52663
52664 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52665     rowClass : "x-grid-row",
52666     cellClass : "x-grid-cell",
52667     tdClass : "x-grid-td",
52668     hdClass : "x-grid-hd",
52669     splitClass : "x-grid-hd-split",
52670     
52671     init: function(grid){
52672         this.grid = grid;
52673                 var cid = this.grid.getGridEl().id;
52674         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52675         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52676         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52677         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52678         },
52679         
52680     getColumnRenderers : function(){
52681         var renderers = [];
52682         var cm = this.grid.colModel;
52683         var colCount = cm.getColumnCount();
52684         for(var i = 0; i < colCount; i++){
52685             renderers[i] = cm.getRenderer(i);
52686         }
52687         return renderers;
52688     },
52689     
52690     getColumnIds : function(){
52691         var ids = [];
52692         var cm = this.grid.colModel;
52693         var colCount = cm.getColumnCount();
52694         for(var i = 0; i < colCount; i++){
52695             ids[i] = cm.getColumnId(i);
52696         }
52697         return ids;
52698     },
52699     
52700     getDataIndexes : function(){
52701         if(!this.indexMap){
52702             this.indexMap = this.buildIndexMap();
52703         }
52704         return this.indexMap.colToData;
52705     },
52706     
52707     getColumnIndexByDataIndex : function(dataIndex){
52708         if(!this.indexMap){
52709             this.indexMap = this.buildIndexMap();
52710         }
52711         return this.indexMap.dataToCol[dataIndex];
52712     },
52713     
52714     /**
52715      * Set a css style for a column dynamically. 
52716      * @param {Number} colIndex The index of the column
52717      * @param {String} name The css property name
52718      * @param {String} value The css value
52719      */
52720     setCSSStyle : function(colIndex, name, value){
52721         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52722         Roo.util.CSS.updateRule(selector, name, value);
52723     },
52724     
52725     generateRules : function(cm){
52726         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52727         Roo.util.CSS.removeStyleSheet(rulesId);
52728         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52729             var cid = cm.getColumnId(i);
52730             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52731                          this.tdSelector, cid, " {\n}\n",
52732                          this.hdSelector, cid, " {\n}\n",
52733                          this.splitSelector, cid, " {\n}\n");
52734         }
52735         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52736     }
52737 });/*
52738  * Based on:
52739  * Ext JS Library 1.1.1
52740  * Copyright(c) 2006-2007, Ext JS, LLC.
52741  *
52742  * Originally Released Under LGPL - original licence link has changed is not relivant.
52743  *
52744  * Fork - LGPL
52745  * <script type="text/javascript">
52746  */
52747
52748 // private
52749 // This is a support class used internally by the Grid components
52750 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52751     this.grid = grid;
52752     this.view = grid.getView();
52753     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52754     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52755     if(hd2){
52756         this.setHandleElId(Roo.id(hd));
52757         this.setOuterHandleElId(Roo.id(hd2));
52758     }
52759     this.scroll = false;
52760 };
52761 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52762     maxDragWidth: 120,
52763     getDragData : function(e){
52764         var t = Roo.lib.Event.getTarget(e);
52765         var h = this.view.findHeaderCell(t);
52766         if(h){
52767             return {ddel: h.firstChild, header:h};
52768         }
52769         return false;
52770     },
52771
52772     onInitDrag : function(e){
52773         this.view.headersDisabled = true;
52774         var clone = this.dragData.ddel.cloneNode(true);
52775         clone.id = Roo.id();
52776         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52777         this.proxy.update(clone);
52778         return true;
52779     },
52780
52781     afterValidDrop : function(){
52782         var v = this.view;
52783         setTimeout(function(){
52784             v.headersDisabled = false;
52785         }, 50);
52786     },
52787
52788     afterInvalidDrop : function(){
52789         var v = this.view;
52790         setTimeout(function(){
52791             v.headersDisabled = false;
52792         }, 50);
52793     }
52794 });
52795 /*
52796  * Based on:
52797  * Ext JS Library 1.1.1
52798  * Copyright(c) 2006-2007, Ext JS, LLC.
52799  *
52800  * Originally Released Under LGPL - original licence link has changed is not relivant.
52801  *
52802  * Fork - LGPL
52803  * <script type="text/javascript">
52804  */
52805 // private
52806 // This is a support class used internally by the Grid components
52807 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52808     this.grid = grid;
52809     this.view = grid.getView();
52810     // split the proxies so they don't interfere with mouse events
52811     this.proxyTop = Roo.DomHelper.append(document.body, {
52812         cls:"col-move-top", html:"&#160;"
52813     }, true);
52814     this.proxyBottom = Roo.DomHelper.append(document.body, {
52815         cls:"col-move-bottom", html:"&#160;"
52816     }, true);
52817     this.proxyTop.hide = this.proxyBottom.hide = function(){
52818         this.setLeftTop(-100,-100);
52819         this.setStyle("visibility", "hidden");
52820     };
52821     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52822     // temporarily disabled
52823     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52824     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52825 };
52826 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52827     proxyOffsets : [-4, -9],
52828     fly: Roo.Element.fly,
52829
52830     getTargetFromEvent : function(e){
52831         var t = Roo.lib.Event.getTarget(e);
52832         var cindex = this.view.findCellIndex(t);
52833         if(cindex !== false){
52834             return this.view.getHeaderCell(cindex);
52835         }
52836         return null;
52837     },
52838
52839     nextVisible : function(h){
52840         var v = this.view, cm = this.grid.colModel;
52841         h = h.nextSibling;
52842         while(h){
52843             if(!cm.isHidden(v.getCellIndex(h))){
52844                 return h;
52845             }
52846             h = h.nextSibling;
52847         }
52848         return null;
52849     },
52850
52851     prevVisible : function(h){
52852         var v = this.view, cm = this.grid.colModel;
52853         h = h.prevSibling;
52854         while(h){
52855             if(!cm.isHidden(v.getCellIndex(h))){
52856                 return h;
52857             }
52858             h = h.prevSibling;
52859         }
52860         return null;
52861     },
52862
52863     positionIndicator : function(h, n, e){
52864         var x = Roo.lib.Event.getPageX(e);
52865         var r = Roo.lib.Dom.getRegion(n.firstChild);
52866         var px, pt, py = r.top + this.proxyOffsets[1];
52867         if((r.right - x) <= (r.right-r.left)/2){
52868             px = r.right+this.view.borderWidth;
52869             pt = "after";
52870         }else{
52871             px = r.left;
52872             pt = "before";
52873         }
52874         var oldIndex = this.view.getCellIndex(h);
52875         var newIndex = this.view.getCellIndex(n);
52876
52877         if(this.grid.colModel.isFixed(newIndex)){
52878             return false;
52879         }
52880
52881         var locked = this.grid.colModel.isLocked(newIndex);
52882
52883         if(pt == "after"){
52884             newIndex++;
52885         }
52886         if(oldIndex < newIndex){
52887             newIndex--;
52888         }
52889         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52890             return false;
52891         }
52892         px +=  this.proxyOffsets[0];
52893         this.proxyTop.setLeftTop(px, py);
52894         this.proxyTop.show();
52895         if(!this.bottomOffset){
52896             this.bottomOffset = this.view.mainHd.getHeight();
52897         }
52898         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52899         this.proxyBottom.show();
52900         return pt;
52901     },
52902
52903     onNodeEnter : function(n, dd, e, data){
52904         if(data.header != n){
52905             this.positionIndicator(data.header, n, e);
52906         }
52907     },
52908
52909     onNodeOver : function(n, dd, e, data){
52910         var result = false;
52911         if(data.header != n){
52912             result = this.positionIndicator(data.header, n, e);
52913         }
52914         if(!result){
52915             this.proxyTop.hide();
52916             this.proxyBottom.hide();
52917         }
52918         return result ? this.dropAllowed : this.dropNotAllowed;
52919     },
52920
52921     onNodeOut : function(n, dd, e, data){
52922         this.proxyTop.hide();
52923         this.proxyBottom.hide();
52924     },
52925
52926     onNodeDrop : function(n, dd, e, data){
52927         var h = data.header;
52928         if(h != n){
52929             var cm = this.grid.colModel;
52930             var x = Roo.lib.Event.getPageX(e);
52931             var r = Roo.lib.Dom.getRegion(n.firstChild);
52932             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52933             var oldIndex = this.view.getCellIndex(h);
52934             var newIndex = this.view.getCellIndex(n);
52935             var locked = cm.isLocked(newIndex);
52936             if(pt == "after"){
52937                 newIndex++;
52938             }
52939             if(oldIndex < newIndex){
52940                 newIndex--;
52941             }
52942             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52943                 return false;
52944             }
52945             cm.setLocked(oldIndex, locked, true);
52946             cm.moveColumn(oldIndex, newIndex);
52947             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52948             return true;
52949         }
52950         return false;
52951     }
52952 });
52953 /*
52954  * Based on:
52955  * Ext JS Library 1.1.1
52956  * Copyright(c) 2006-2007, Ext JS, LLC.
52957  *
52958  * Originally Released Under LGPL - original licence link has changed is not relivant.
52959  *
52960  * Fork - LGPL
52961  * <script type="text/javascript">
52962  */
52963   
52964 /**
52965  * @class Roo.grid.GridView
52966  * @extends Roo.util.Observable
52967  *
52968  * @constructor
52969  * @param {Object} config
52970  */
52971 Roo.grid.GridView = function(config){
52972     Roo.grid.GridView.superclass.constructor.call(this);
52973     this.el = null;
52974
52975     Roo.apply(this, config);
52976 };
52977
52978 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52979
52980     unselectable :  'unselectable="on"',
52981     unselectableCls :  'x-unselectable',
52982     
52983     
52984     rowClass : "x-grid-row",
52985
52986     cellClass : "x-grid-col",
52987
52988     tdClass : "x-grid-td",
52989
52990     hdClass : "x-grid-hd",
52991
52992     splitClass : "x-grid-split",
52993
52994     sortClasses : ["sort-asc", "sort-desc"],
52995
52996     enableMoveAnim : false,
52997
52998     hlColor: "C3DAF9",
52999
53000     dh : Roo.DomHelper,
53001
53002     fly : Roo.Element.fly,
53003
53004     css : Roo.util.CSS,
53005
53006     borderWidth: 1,
53007
53008     splitOffset: 3,
53009
53010     scrollIncrement : 22,
53011
53012     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53013
53014     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53015
53016     bind : function(ds, cm){
53017         if(this.ds){
53018             this.ds.un("load", this.onLoad, this);
53019             this.ds.un("datachanged", this.onDataChange, this);
53020             this.ds.un("add", this.onAdd, this);
53021             this.ds.un("remove", this.onRemove, this);
53022             this.ds.un("update", this.onUpdate, this);
53023             this.ds.un("clear", this.onClear, this);
53024         }
53025         if(ds){
53026             ds.on("load", this.onLoad, this);
53027             ds.on("datachanged", this.onDataChange, this);
53028             ds.on("add", this.onAdd, this);
53029             ds.on("remove", this.onRemove, this);
53030             ds.on("update", this.onUpdate, this);
53031             ds.on("clear", this.onClear, this);
53032         }
53033         this.ds = ds;
53034
53035         if(this.cm){
53036             this.cm.un("widthchange", this.onColWidthChange, this);
53037             this.cm.un("headerchange", this.onHeaderChange, this);
53038             this.cm.un("hiddenchange", this.onHiddenChange, this);
53039             this.cm.un("columnmoved", this.onColumnMove, this);
53040             this.cm.un("columnlockchange", this.onColumnLock, this);
53041         }
53042         if(cm){
53043             this.generateRules(cm);
53044             cm.on("widthchange", this.onColWidthChange, this);
53045             cm.on("headerchange", this.onHeaderChange, this);
53046             cm.on("hiddenchange", this.onHiddenChange, this);
53047             cm.on("columnmoved", this.onColumnMove, this);
53048             cm.on("columnlockchange", this.onColumnLock, this);
53049         }
53050         this.cm = cm;
53051     },
53052
53053     init: function(grid){
53054         Roo.grid.GridView.superclass.init.call(this, grid);
53055
53056         this.bind(grid.dataSource, grid.colModel);
53057
53058         grid.on("headerclick", this.handleHeaderClick, this);
53059
53060         if(grid.trackMouseOver){
53061             grid.on("mouseover", this.onRowOver, this);
53062             grid.on("mouseout", this.onRowOut, this);
53063         }
53064         grid.cancelTextSelection = function(){};
53065         this.gridId = grid.id;
53066
53067         var tpls = this.templates || {};
53068
53069         if(!tpls.master){
53070             tpls.master = new Roo.Template(
53071                '<div class="x-grid" hidefocus="true">',
53072                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53073                   '<div class="x-grid-topbar"></div>',
53074                   '<div class="x-grid-scroller"><div></div></div>',
53075                   '<div class="x-grid-locked">',
53076                       '<div class="x-grid-header">{lockedHeader}</div>',
53077                       '<div class="x-grid-body">{lockedBody}</div>',
53078                   "</div>",
53079                   '<div class="x-grid-viewport">',
53080                       '<div class="x-grid-header">{header}</div>',
53081                       '<div class="x-grid-body">{body}</div>',
53082                   "</div>",
53083                   '<div class="x-grid-bottombar"></div>',
53084                  
53085                   '<div class="x-grid-resize-proxy">&#160;</div>',
53086                "</div>"
53087             );
53088             tpls.master.disableformats = true;
53089         }
53090
53091         if(!tpls.header){
53092             tpls.header = new Roo.Template(
53093                '<table border="0" cellspacing="0" cellpadding="0">',
53094                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53095                "</table>{splits}"
53096             );
53097             tpls.header.disableformats = true;
53098         }
53099         tpls.header.compile();
53100
53101         if(!tpls.hcell){
53102             tpls.hcell = new Roo.Template(
53103                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53104                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53105                 "</div></td>"
53106              );
53107              tpls.hcell.disableFormats = true;
53108         }
53109         tpls.hcell.compile();
53110
53111         if(!tpls.hsplit){
53112             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53113                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53114             tpls.hsplit.disableFormats = true;
53115         }
53116         tpls.hsplit.compile();
53117
53118         if(!tpls.body){
53119             tpls.body = new Roo.Template(
53120                '<table border="0" cellspacing="0" cellpadding="0">',
53121                "<tbody>{rows}</tbody>",
53122                "</table>"
53123             );
53124             tpls.body.disableFormats = true;
53125         }
53126         tpls.body.compile();
53127
53128         if(!tpls.row){
53129             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53130             tpls.row.disableFormats = true;
53131         }
53132         tpls.row.compile();
53133
53134         if(!tpls.cell){
53135             tpls.cell = new Roo.Template(
53136                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53137                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53138                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53139                 "</td>"
53140             );
53141             tpls.cell.disableFormats = true;
53142         }
53143         tpls.cell.compile();
53144
53145         this.templates = tpls;
53146     },
53147
53148     // remap these for backwards compat
53149     onColWidthChange : function(){
53150         this.updateColumns.apply(this, arguments);
53151     },
53152     onHeaderChange : function(){
53153         this.updateHeaders.apply(this, arguments);
53154     }, 
53155     onHiddenChange : function(){
53156         this.handleHiddenChange.apply(this, arguments);
53157     },
53158     onColumnMove : function(){
53159         this.handleColumnMove.apply(this, arguments);
53160     },
53161     onColumnLock : function(){
53162         this.handleLockChange.apply(this, arguments);
53163     },
53164
53165     onDataChange : function(){
53166         this.refresh();
53167         this.updateHeaderSortState();
53168     },
53169
53170     onClear : function(){
53171         this.refresh();
53172     },
53173
53174     onUpdate : function(ds, record){
53175         this.refreshRow(record);
53176     },
53177
53178     refreshRow : function(record){
53179         var ds = this.ds, index;
53180         if(typeof record == 'number'){
53181             index = record;
53182             record = ds.getAt(index);
53183         }else{
53184             index = ds.indexOf(record);
53185         }
53186         this.insertRows(ds, index, index, true);
53187         this.onRemove(ds, record, index+1, true);
53188         this.syncRowHeights(index, index);
53189         this.layout();
53190         this.fireEvent("rowupdated", this, index, record);
53191     },
53192
53193     onAdd : function(ds, records, index){
53194         this.insertRows(ds, index, index + (records.length-1));
53195     },
53196
53197     onRemove : function(ds, record, index, isUpdate){
53198         if(isUpdate !== true){
53199             this.fireEvent("beforerowremoved", this, index, record);
53200         }
53201         var bt = this.getBodyTable(), lt = this.getLockedTable();
53202         if(bt.rows[index]){
53203             bt.firstChild.removeChild(bt.rows[index]);
53204         }
53205         if(lt.rows[index]){
53206             lt.firstChild.removeChild(lt.rows[index]);
53207         }
53208         if(isUpdate !== true){
53209             this.stripeRows(index);
53210             this.syncRowHeights(index, index);
53211             this.layout();
53212             this.fireEvent("rowremoved", this, index, record);
53213         }
53214     },
53215
53216     onLoad : function(){
53217         this.scrollToTop();
53218     },
53219
53220     /**
53221      * Scrolls the grid to the top
53222      */
53223     scrollToTop : function(){
53224         if(this.scroller){
53225             this.scroller.dom.scrollTop = 0;
53226             this.syncScroll();
53227         }
53228     },
53229
53230     /**
53231      * Gets a panel in the header of the grid that can be used for toolbars etc.
53232      * After modifying the contents of this panel a call to grid.autoSize() may be
53233      * required to register any changes in size.
53234      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53235      * @return Roo.Element
53236      */
53237     getHeaderPanel : function(doShow){
53238         if(doShow){
53239             this.headerPanel.show();
53240         }
53241         return this.headerPanel;
53242     },
53243
53244     /**
53245      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53246      * After modifying the contents of this panel a call to grid.autoSize() may be
53247      * required to register any changes in size.
53248      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53249      * @return Roo.Element
53250      */
53251     getFooterPanel : function(doShow){
53252         if(doShow){
53253             this.footerPanel.show();
53254         }
53255         return this.footerPanel;
53256     },
53257
53258     initElements : function(){
53259         var E = Roo.Element;
53260         var el = this.grid.getGridEl().dom.firstChild;
53261         var cs = el.childNodes;
53262
53263         this.el = new E(el);
53264         
53265          this.focusEl = new E(el.firstChild);
53266         this.focusEl.swallowEvent("click", true);
53267         
53268         this.headerPanel = new E(cs[1]);
53269         this.headerPanel.enableDisplayMode("block");
53270
53271         this.scroller = new E(cs[2]);
53272         this.scrollSizer = new E(this.scroller.dom.firstChild);
53273
53274         this.lockedWrap = new E(cs[3]);
53275         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53276         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53277
53278         this.mainWrap = new E(cs[4]);
53279         this.mainHd = new E(this.mainWrap.dom.firstChild);
53280         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53281
53282         this.footerPanel = new E(cs[5]);
53283         this.footerPanel.enableDisplayMode("block");
53284
53285         this.resizeProxy = new E(cs[6]);
53286
53287         this.headerSelector = String.format(
53288            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53289            this.lockedHd.id, this.mainHd.id
53290         );
53291
53292         this.splitterSelector = String.format(
53293            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53294            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53295         );
53296     },
53297     idToCssName : function(s)
53298     {
53299         return s.replace(/[^a-z0-9]+/ig, '-');
53300     },
53301
53302     getHeaderCell : function(index){
53303         return Roo.DomQuery.select(this.headerSelector)[index];
53304     },
53305
53306     getHeaderCellMeasure : function(index){
53307         return this.getHeaderCell(index).firstChild;
53308     },
53309
53310     getHeaderCellText : function(index){
53311         return this.getHeaderCell(index).firstChild.firstChild;
53312     },
53313
53314     getLockedTable : function(){
53315         return this.lockedBody.dom.firstChild;
53316     },
53317
53318     getBodyTable : function(){
53319         return this.mainBody.dom.firstChild;
53320     },
53321
53322     getLockedRow : function(index){
53323         return this.getLockedTable().rows[index];
53324     },
53325
53326     getRow : function(index){
53327         return this.getBodyTable().rows[index];
53328     },
53329
53330     getRowComposite : function(index){
53331         if(!this.rowEl){
53332             this.rowEl = new Roo.CompositeElementLite();
53333         }
53334         var els = [], lrow, mrow;
53335         if(lrow = this.getLockedRow(index)){
53336             els.push(lrow);
53337         }
53338         if(mrow = this.getRow(index)){
53339             els.push(mrow);
53340         }
53341         this.rowEl.elements = els;
53342         return this.rowEl;
53343     },
53344     /**
53345      * Gets the 'td' of the cell
53346      * 
53347      * @param {Integer} rowIndex row to select
53348      * @param {Integer} colIndex column to select
53349      * 
53350      * @return {Object} 
53351      */
53352     getCell : function(rowIndex, colIndex){
53353         var locked = this.cm.getLockedCount();
53354         var source;
53355         if(colIndex < locked){
53356             source = this.lockedBody.dom.firstChild;
53357         }else{
53358             source = this.mainBody.dom.firstChild;
53359             colIndex -= locked;
53360         }
53361         return source.rows[rowIndex].childNodes[colIndex];
53362     },
53363
53364     getCellText : function(rowIndex, colIndex){
53365         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53366     },
53367
53368     getCellBox : function(cell){
53369         var b = this.fly(cell).getBox();
53370         if(Roo.isOpera){ // opera fails to report the Y
53371             b.y = cell.offsetTop + this.mainBody.getY();
53372         }
53373         return b;
53374     },
53375
53376     getCellIndex : function(cell){
53377         var id = String(cell.className).match(this.cellRE);
53378         if(id){
53379             return parseInt(id[1], 10);
53380         }
53381         return 0;
53382     },
53383
53384     findHeaderIndex : function(n){
53385         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53386         return r ? this.getCellIndex(r) : false;
53387     },
53388
53389     findHeaderCell : function(n){
53390         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53391         return r ? r : false;
53392     },
53393
53394     findRowIndex : function(n){
53395         if(!n){
53396             return false;
53397         }
53398         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53399         return r ? r.rowIndex : false;
53400     },
53401
53402     findCellIndex : function(node){
53403         var stop = this.el.dom;
53404         while(node && node != stop){
53405             if(this.findRE.test(node.className)){
53406                 return this.getCellIndex(node);
53407             }
53408             node = node.parentNode;
53409         }
53410         return false;
53411     },
53412
53413     getColumnId : function(index){
53414         return this.cm.getColumnId(index);
53415     },
53416
53417     getSplitters : function()
53418     {
53419         if(this.splitterSelector){
53420            return Roo.DomQuery.select(this.splitterSelector);
53421         }else{
53422             return null;
53423       }
53424     },
53425
53426     getSplitter : function(index){
53427         return this.getSplitters()[index];
53428     },
53429
53430     onRowOver : function(e, t){
53431         var row;
53432         if((row = this.findRowIndex(t)) !== false){
53433             this.getRowComposite(row).addClass("x-grid-row-over");
53434         }
53435     },
53436
53437     onRowOut : function(e, t){
53438         var row;
53439         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53440             this.getRowComposite(row).removeClass("x-grid-row-over");
53441         }
53442     },
53443
53444     renderHeaders : function(){
53445         var cm = this.cm;
53446         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53447         var cb = [], lb = [], sb = [], lsb = [], p = {};
53448         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53449             p.cellId = "x-grid-hd-0-" + i;
53450             p.splitId = "x-grid-csplit-0-" + i;
53451             p.id = cm.getColumnId(i);
53452             p.title = cm.getColumnTooltip(i) || "";
53453             p.value = cm.getColumnHeader(i) || "";
53454             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53455             if(!cm.isLocked(i)){
53456                 cb[cb.length] = ct.apply(p);
53457                 sb[sb.length] = st.apply(p);
53458             }else{
53459                 lb[lb.length] = ct.apply(p);
53460                 lsb[lsb.length] = st.apply(p);
53461             }
53462         }
53463         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53464                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53465     },
53466
53467     updateHeaders : function(){
53468         var html = this.renderHeaders();
53469         this.lockedHd.update(html[0]);
53470         this.mainHd.update(html[1]);
53471     },
53472
53473     /**
53474      * Focuses the specified row.
53475      * @param {Number} row The row index
53476      */
53477     focusRow : function(row)
53478     {
53479         //Roo.log('GridView.focusRow');
53480         var x = this.scroller.dom.scrollLeft;
53481         this.focusCell(row, 0, false);
53482         this.scroller.dom.scrollLeft = x;
53483     },
53484
53485     /**
53486      * Focuses the specified cell.
53487      * @param {Number} row The row index
53488      * @param {Number} col The column index
53489      * @param {Boolean} hscroll false to disable horizontal scrolling
53490      */
53491     focusCell : function(row, col, hscroll)
53492     {
53493         //Roo.log('GridView.focusCell');
53494         var el = this.ensureVisible(row, col, hscroll);
53495         this.focusEl.alignTo(el, "tl-tl");
53496         if(Roo.isGecko){
53497             this.focusEl.focus();
53498         }else{
53499             this.focusEl.focus.defer(1, this.focusEl);
53500         }
53501     },
53502
53503     /**
53504      * Scrolls the specified cell into view
53505      * @param {Number} row The row index
53506      * @param {Number} col The column index
53507      * @param {Boolean} hscroll false to disable horizontal scrolling
53508      */
53509     ensureVisible : function(row, col, hscroll)
53510     {
53511         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53512         //return null; //disable for testing.
53513         if(typeof row != "number"){
53514             row = row.rowIndex;
53515         }
53516         if(row < 0 && row >= this.ds.getCount()){
53517             return  null;
53518         }
53519         col = (col !== undefined ? col : 0);
53520         var cm = this.grid.colModel;
53521         while(cm.isHidden(col)){
53522             col++;
53523         }
53524
53525         var el = this.getCell(row, col);
53526         if(!el){
53527             return null;
53528         }
53529         var c = this.scroller.dom;
53530
53531         var ctop = parseInt(el.offsetTop, 10);
53532         var cleft = parseInt(el.offsetLeft, 10);
53533         var cbot = ctop + el.offsetHeight;
53534         var cright = cleft + el.offsetWidth;
53535         
53536         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53537         var stop = parseInt(c.scrollTop, 10);
53538         var sleft = parseInt(c.scrollLeft, 10);
53539         var sbot = stop + ch;
53540         var sright = sleft + c.clientWidth;
53541         /*
53542         Roo.log('GridView.ensureVisible:' +
53543                 ' ctop:' + ctop +
53544                 ' c.clientHeight:' + c.clientHeight +
53545                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53546                 ' stop:' + stop +
53547                 ' cbot:' + cbot +
53548                 ' sbot:' + sbot +
53549                 ' ch:' + ch  
53550                 );
53551         */
53552         if(ctop < stop){
53553              c.scrollTop = ctop;
53554             //Roo.log("set scrolltop to ctop DISABLE?");
53555         }else if(cbot > sbot){
53556             //Roo.log("set scrolltop to cbot-ch");
53557             c.scrollTop = cbot-ch;
53558         }
53559         
53560         if(hscroll !== false){
53561             if(cleft < sleft){
53562                 c.scrollLeft = cleft;
53563             }else if(cright > sright){
53564                 c.scrollLeft = cright-c.clientWidth;
53565             }
53566         }
53567          
53568         return el;
53569     },
53570
53571     updateColumns : function(){
53572         this.grid.stopEditing();
53573         var cm = this.grid.colModel, colIds = this.getColumnIds();
53574         //var totalWidth = cm.getTotalWidth();
53575         var pos = 0;
53576         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53577             //if(cm.isHidden(i)) continue;
53578             var w = cm.getColumnWidth(i);
53579             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53580             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53581         }
53582         this.updateSplitters();
53583     },
53584
53585     generateRules : function(cm){
53586         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53587         Roo.util.CSS.removeStyleSheet(rulesId);
53588         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53589             var cid = cm.getColumnId(i);
53590             var align = '';
53591             if(cm.config[i].align){
53592                 align = 'text-align:'+cm.config[i].align+';';
53593             }
53594             var hidden = '';
53595             if(cm.isHidden(i)){
53596                 hidden = 'display:none;';
53597             }
53598             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53599             ruleBuf.push(
53600                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53601                     this.hdSelector, cid, " {\n", align, width, "}\n",
53602                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53603                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53604         }
53605         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53606     },
53607
53608     updateSplitters : function(){
53609         var cm = this.cm, s = this.getSplitters();
53610         if(s){ // splitters not created yet
53611             var pos = 0, locked = true;
53612             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53613                 if(cm.isHidden(i)) continue;
53614                 var w = cm.getColumnWidth(i); // make sure it's a number
53615                 if(!cm.isLocked(i) && locked){
53616                     pos = 0;
53617                     locked = false;
53618                 }
53619                 pos += w;
53620                 s[i].style.left = (pos-this.splitOffset) + "px";
53621             }
53622         }
53623     },
53624
53625     handleHiddenChange : function(colModel, colIndex, hidden){
53626         if(hidden){
53627             this.hideColumn(colIndex);
53628         }else{
53629             this.unhideColumn(colIndex);
53630         }
53631     },
53632
53633     hideColumn : function(colIndex){
53634         var cid = this.getColumnId(colIndex);
53635         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53636         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53637         if(Roo.isSafari){
53638             this.updateHeaders();
53639         }
53640         this.updateSplitters();
53641         this.layout();
53642     },
53643
53644     unhideColumn : function(colIndex){
53645         var cid = this.getColumnId(colIndex);
53646         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53647         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53648
53649         if(Roo.isSafari){
53650             this.updateHeaders();
53651         }
53652         this.updateSplitters();
53653         this.layout();
53654     },
53655
53656     insertRows : function(dm, firstRow, lastRow, isUpdate){
53657         if(firstRow == 0 && lastRow == dm.getCount()-1){
53658             this.refresh();
53659         }else{
53660             if(!isUpdate){
53661                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53662             }
53663             var s = this.getScrollState();
53664             var markup = this.renderRows(firstRow, lastRow);
53665             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53666             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53667             this.restoreScroll(s);
53668             if(!isUpdate){
53669                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53670                 this.syncRowHeights(firstRow, lastRow);
53671                 this.stripeRows(firstRow);
53672                 this.layout();
53673             }
53674         }
53675     },
53676
53677     bufferRows : function(markup, target, index){
53678         var before = null, trows = target.rows, tbody = target.tBodies[0];
53679         if(index < trows.length){
53680             before = trows[index];
53681         }
53682         var b = document.createElement("div");
53683         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53684         var rows = b.firstChild.rows;
53685         for(var i = 0, len = rows.length; i < len; i++){
53686             if(before){
53687                 tbody.insertBefore(rows[0], before);
53688             }else{
53689                 tbody.appendChild(rows[0]);
53690             }
53691         }
53692         b.innerHTML = "";
53693         b = null;
53694     },
53695
53696     deleteRows : function(dm, firstRow, lastRow){
53697         if(dm.getRowCount()<1){
53698             this.fireEvent("beforerefresh", this);
53699             this.mainBody.update("");
53700             this.lockedBody.update("");
53701             this.fireEvent("refresh", this);
53702         }else{
53703             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53704             var bt = this.getBodyTable();
53705             var tbody = bt.firstChild;
53706             var rows = bt.rows;
53707             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53708                 tbody.removeChild(rows[firstRow]);
53709             }
53710             this.stripeRows(firstRow);
53711             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53712         }
53713     },
53714
53715     updateRows : function(dataSource, firstRow, lastRow){
53716         var s = this.getScrollState();
53717         this.refresh();
53718         this.restoreScroll(s);
53719     },
53720
53721     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53722         if(!noRefresh){
53723            this.refresh();
53724         }
53725         this.updateHeaderSortState();
53726     },
53727
53728     getScrollState : function(){
53729         
53730         var sb = this.scroller.dom;
53731         return {left: sb.scrollLeft, top: sb.scrollTop};
53732     },
53733
53734     stripeRows : function(startRow){
53735         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53736             return;
53737         }
53738         startRow = startRow || 0;
53739         var rows = this.getBodyTable().rows;
53740         var lrows = this.getLockedTable().rows;
53741         var cls = ' x-grid-row-alt ';
53742         for(var i = startRow, len = rows.length; i < len; i++){
53743             var row = rows[i], lrow = lrows[i];
53744             var isAlt = ((i+1) % 2 == 0);
53745             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53746             if(isAlt == hasAlt){
53747                 continue;
53748             }
53749             if(isAlt){
53750                 row.className += " x-grid-row-alt";
53751             }else{
53752                 row.className = row.className.replace("x-grid-row-alt", "");
53753             }
53754             if(lrow){
53755                 lrow.className = row.className;
53756             }
53757         }
53758     },
53759
53760     restoreScroll : function(state){
53761         //Roo.log('GridView.restoreScroll');
53762         var sb = this.scroller.dom;
53763         sb.scrollLeft = state.left;
53764         sb.scrollTop = state.top;
53765         this.syncScroll();
53766     },
53767
53768     syncScroll : function(){
53769         //Roo.log('GridView.syncScroll');
53770         var sb = this.scroller.dom;
53771         var sh = this.mainHd.dom;
53772         var bs = this.mainBody.dom;
53773         var lv = this.lockedBody.dom;
53774         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53775         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53776     },
53777
53778     handleScroll : function(e){
53779         this.syncScroll();
53780         var sb = this.scroller.dom;
53781         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53782         e.stopEvent();
53783     },
53784
53785     handleWheel : function(e){
53786         var d = e.getWheelDelta();
53787         this.scroller.dom.scrollTop -= d*22;
53788         // set this here to prevent jumpy scrolling on large tables
53789         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53790         e.stopEvent();
53791     },
53792
53793     renderRows : function(startRow, endRow){
53794         // pull in all the crap needed to render rows
53795         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53796         var colCount = cm.getColumnCount();
53797
53798         if(ds.getCount() < 1){
53799             return ["", ""];
53800         }
53801
53802         // build a map for all the columns
53803         var cs = [];
53804         for(var i = 0; i < colCount; i++){
53805             var name = cm.getDataIndex(i);
53806             cs[i] = {
53807                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53808                 renderer : cm.getRenderer(i),
53809                 id : cm.getColumnId(i),
53810                 locked : cm.isLocked(i)
53811             };
53812         }
53813
53814         startRow = startRow || 0;
53815         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53816
53817         // records to render
53818         var rs = ds.getRange(startRow, endRow);
53819
53820         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53821     },
53822
53823     // As much as I hate to duplicate code, this was branched because FireFox really hates
53824     // [].join("") on strings. The performance difference was substantial enough to
53825     // branch this function
53826     doRender : Roo.isGecko ?
53827             function(cs, rs, ds, startRow, colCount, stripe){
53828                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53829                 // buffers
53830                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53831                 
53832                 var hasListener = this.grid.hasListener('rowclass');
53833                 var rowcfg = {};
53834                 for(var j = 0, len = rs.length; j < len; j++){
53835                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53836                     for(var i = 0; i < colCount; i++){
53837                         c = cs[i];
53838                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53839                         p.id = c.id;
53840                         p.css = p.attr = "";
53841                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53842                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53843                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53844                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53845                         }
53846                         var markup = ct.apply(p);
53847                         if(!c.locked){
53848                             cb+= markup;
53849                         }else{
53850                             lcb+= markup;
53851                         }
53852                     }
53853                     var alt = [];
53854                     if(stripe && ((rowIndex+1) % 2 == 0)){
53855                         alt.push("x-grid-row-alt")
53856                     }
53857                     if(r.dirty){
53858                         alt.push(  " x-grid-dirty-row");
53859                     }
53860                     rp.cells = lcb;
53861                     if(this.getRowClass){
53862                         alt.push(this.getRowClass(r, rowIndex));
53863                     }
53864                     if (hasListener) {
53865                         rowcfg = {
53866                              
53867                             record: r,
53868                             rowIndex : rowIndex,
53869                             rowClass : ''
53870                         }
53871                         this.grid.fireEvent('rowclass', this, rowcfg);
53872                         alt.push(rowcfg.rowClass);
53873                     }
53874                     rp.alt = alt.join(" ");
53875                     lbuf+= rt.apply(rp);
53876                     rp.cells = cb;
53877                     buf+=  rt.apply(rp);
53878                 }
53879                 return [lbuf, buf];
53880             } :
53881             function(cs, rs, ds, startRow, colCount, stripe){
53882                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53883                 // buffers
53884                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53885                 var hasListener = this.grid.hasListener('rowclass');
53886  
53887                 var rowcfg = {};
53888                 for(var j = 0, len = rs.length; j < len; j++){
53889                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53890                     for(var i = 0; i < colCount; i++){
53891                         c = cs[i];
53892                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53893                         p.id = c.id;
53894                         p.css = p.attr = "";
53895                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53896                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53897                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53898                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53899                         }
53900                         
53901                         var markup = ct.apply(p);
53902                         if(!c.locked){
53903                             cb[cb.length] = markup;
53904                         }else{
53905                             lcb[lcb.length] = markup;
53906                         }
53907                     }
53908                     var alt = [];
53909                     if(stripe && ((rowIndex+1) % 2 == 0)){
53910                         alt.push( "x-grid-row-alt");
53911                     }
53912                     if(r.dirty){
53913                         alt.push(" x-grid-dirty-row");
53914                     }
53915                     rp.cells = lcb;
53916                     if(this.getRowClass){
53917                         alt.push( this.getRowClass(r, rowIndex));
53918                     }
53919                     if (hasListener) {
53920                         rowcfg = {
53921                              
53922                             record: r,
53923                             rowIndex : rowIndex,
53924                             rowClass : ''
53925                         }
53926                         this.grid.fireEvent('rowclass', this, rowcfg);
53927                         alt.push(rowcfg.rowClass);
53928                     }
53929                     rp.alt = alt.join(" ");
53930                     rp.cells = lcb.join("");
53931                     lbuf[lbuf.length] = rt.apply(rp);
53932                     rp.cells = cb.join("");
53933                     buf[buf.length] =  rt.apply(rp);
53934                 }
53935                 return [lbuf.join(""), buf.join("")];
53936             },
53937
53938     renderBody : function(){
53939         var markup = this.renderRows();
53940         var bt = this.templates.body;
53941         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53942     },
53943
53944     /**
53945      * Refreshes the grid
53946      * @param {Boolean} headersToo
53947      */
53948     refresh : function(headersToo){
53949         this.fireEvent("beforerefresh", this);
53950         this.grid.stopEditing();
53951         var result = this.renderBody();
53952         this.lockedBody.update(result[0]);
53953         this.mainBody.update(result[1]);
53954         if(headersToo === true){
53955             this.updateHeaders();
53956             this.updateColumns();
53957             this.updateSplitters();
53958             this.updateHeaderSortState();
53959         }
53960         this.syncRowHeights();
53961         this.layout();
53962         this.fireEvent("refresh", this);
53963     },
53964
53965     handleColumnMove : function(cm, oldIndex, newIndex){
53966         this.indexMap = null;
53967         var s = this.getScrollState();
53968         this.refresh(true);
53969         this.restoreScroll(s);
53970         this.afterMove(newIndex);
53971     },
53972
53973     afterMove : function(colIndex){
53974         if(this.enableMoveAnim && Roo.enableFx){
53975             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53976         }
53977         // if multisort - fix sortOrder, and reload..
53978         if (this.grid.dataSource.multiSort) {
53979             // the we can call sort again..
53980             var dm = this.grid.dataSource;
53981             var cm = this.grid.colModel;
53982             var so = [];
53983             for(var i = 0; i < cm.config.length; i++ ) {
53984                 
53985                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53986                     continue; // dont' bother, it's not in sort list or being set.
53987                 }
53988                 
53989                 so.push(cm.config[i].dataIndex);
53990             };
53991             dm.sortOrder = so;
53992             dm.load(dm.lastOptions);
53993             
53994             
53995         }
53996         
53997     },
53998
53999     updateCell : function(dm, rowIndex, dataIndex){
54000         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54001         if(typeof colIndex == "undefined"){ // not present in grid
54002             return;
54003         }
54004         var cm = this.grid.colModel;
54005         var cell = this.getCell(rowIndex, colIndex);
54006         var cellText = this.getCellText(rowIndex, colIndex);
54007
54008         var p = {
54009             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54010             id : cm.getColumnId(colIndex),
54011             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54012         };
54013         var renderer = cm.getRenderer(colIndex);
54014         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54015         if(typeof val == "undefined" || val === "") val = "&#160;";
54016         cellText.innerHTML = val;
54017         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54018         this.syncRowHeights(rowIndex, rowIndex);
54019     },
54020
54021     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54022         var maxWidth = 0;
54023         if(this.grid.autoSizeHeaders){
54024             var h = this.getHeaderCellMeasure(colIndex);
54025             maxWidth = Math.max(maxWidth, h.scrollWidth);
54026         }
54027         var tb, index;
54028         if(this.cm.isLocked(colIndex)){
54029             tb = this.getLockedTable();
54030             index = colIndex;
54031         }else{
54032             tb = this.getBodyTable();
54033             index = colIndex - this.cm.getLockedCount();
54034         }
54035         if(tb && tb.rows){
54036             var rows = tb.rows;
54037             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54038             for(var i = 0; i < stopIndex; i++){
54039                 var cell = rows[i].childNodes[index].firstChild;
54040                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54041             }
54042         }
54043         return maxWidth + /*margin for error in IE*/ 5;
54044     },
54045     /**
54046      * Autofit a column to its content.
54047      * @param {Number} colIndex
54048      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54049      */
54050      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54051          if(this.cm.isHidden(colIndex)){
54052              return; // can't calc a hidden column
54053          }
54054         if(forceMinSize){
54055             var cid = this.cm.getColumnId(colIndex);
54056             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54057            if(this.grid.autoSizeHeaders){
54058                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54059            }
54060         }
54061         var newWidth = this.calcColumnWidth(colIndex);
54062         this.cm.setColumnWidth(colIndex,
54063             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54064         if(!suppressEvent){
54065             this.grid.fireEvent("columnresize", colIndex, newWidth);
54066         }
54067     },
54068
54069     /**
54070      * Autofits all columns to their content and then expands to fit any extra space in the grid
54071      */
54072      autoSizeColumns : function(){
54073         var cm = this.grid.colModel;
54074         var colCount = cm.getColumnCount();
54075         for(var i = 0; i < colCount; i++){
54076             this.autoSizeColumn(i, true, true);
54077         }
54078         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54079             this.fitColumns();
54080         }else{
54081             this.updateColumns();
54082             this.layout();
54083         }
54084     },
54085
54086     /**
54087      * Autofits all columns to the grid's width proportionate with their current size
54088      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54089      */
54090     fitColumns : function(reserveScrollSpace){
54091         var cm = this.grid.colModel;
54092         var colCount = cm.getColumnCount();
54093         var cols = [];
54094         var width = 0;
54095         var i, w;
54096         for (i = 0; i < colCount; i++){
54097             if(!cm.isHidden(i) && !cm.isFixed(i)){
54098                 w = cm.getColumnWidth(i);
54099                 cols.push(i);
54100                 cols.push(w);
54101                 width += w;
54102             }
54103         }
54104         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54105         if(reserveScrollSpace){
54106             avail -= 17;
54107         }
54108         var frac = (avail - cm.getTotalWidth())/width;
54109         while (cols.length){
54110             w = cols.pop();
54111             i = cols.pop();
54112             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54113         }
54114         this.updateColumns();
54115         this.layout();
54116     },
54117
54118     onRowSelect : function(rowIndex){
54119         var row = this.getRowComposite(rowIndex);
54120         row.addClass("x-grid-row-selected");
54121     },
54122
54123     onRowDeselect : function(rowIndex){
54124         var row = this.getRowComposite(rowIndex);
54125         row.removeClass("x-grid-row-selected");
54126     },
54127
54128     onCellSelect : function(row, col){
54129         var cell = this.getCell(row, col);
54130         if(cell){
54131             Roo.fly(cell).addClass("x-grid-cell-selected");
54132         }
54133     },
54134
54135     onCellDeselect : function(row, col){
54136         var cell = this.getCell(row, col);
54137         if(cell){
54138             Roo.fly(cell).removeClass("x-grid-cell-selected");
54139         }
54140     },
54141
54142     updateHeaderSortState : function(){
54143         
54144         // sort state can be single { field: xxx, direction : yyy}
54145         // or   { xxx=>ASC , yyy : DESC ..... }
54146         
54147         var mstate = {};
54148         if (!this.ds.multiSort) { 
54149             var state = this.ds.getSortState();
54150             if(!state){
54151                 return;
54152             }
54153             mstate[state.field] = state.direction;
54154             // FIXME... - this is not used here.. but might be elsewhere..
54155             this.sortState = state;
54156             
54157         } else {
54158             mstate = this.ds.sortToggle;
54159         }
54160         //remove existing sort classes..
54161         
54162         var sc = this.sortClasses;
54163         var hds = this.el.select(this.headerSelector).removeClass(sc);
54164         
54165         for(var f in mstate) {
54166         
54167             var sortColumn = this.cm.findColumnIndex(f);
54168             
54169             if(sortColumn != -1){
54170                 var sortDir = mstate[f];        
54171                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54172             }
54173         }
54174         
54175          
54176         
54177     },
54178
54179
54180     handleHeaderClick : function(g, index,e){
54181         
54182         Roo.log("header click");
54183         
54184         if (Roo.isTouch) {
54185             // touch events on header are handled by context
54186             this.handleHdCtx(g,index,e);
54187             return;
54188         }
54189         
54190         
54191         if(this.headersDisabled){
54192             return;
54193         }
54194         var dm = g.dataSource, cm = g.colModel;
54195         if(!cm.isSortable(index)){
54196             return;
54197         }
54198         g.stopEditing();
54199         
54200         if (dm.multiSort) {
54201             // update the sortOrder
54202             var so = [];
54203             for(var i = 0; i < cm.config.length; i++ ) {
54204                 
54205                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54206                     continue; // dont' bother, it's not in sort list or being set.
54207                 }
54208                 
54209                 so.push(cm.config[i].dataIndex);
54210             };
54211             dm.sortOrder = so;
54212         }
54213         
54214         
54215         dm.sort(cm.getDataIndex(index));
54216     },
54217
54218
54219     destroy : function(){
54220         if(this.colMenu){
54221             this.colMenu.removeAll();
54222             Roo.menu.MenuMgr.unregister(this.colMenu);
54223             this.colMenu.getEl().remove();
54224             delete this.colMenu;
54225         }
54226         if(this.hmenu){
54227             this.hmenu.removeAll();
54228             Roo.menu.MenuMgr.unregister(this.hmenu);
54229             this.hmenu.getEl().remove();
54230             delete this.hmenu;
54231         }
54232         if(this.grid.enableColumnMove){
54233             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54234             if(dds){
54235                 for(var dd in dds){
54236                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54237                         var elid = dds[dd].dragElId;
54238                         dds[dd].unreg();
54239                         Roo.get(elid).remove();
54240                     } else if(dds[dd].config.isTarget){
54241                         dds[dd].proxyTop.remove();
54242                         dds[dd].proxyBottom.remove();
54243                         dds[dd].unreg();
54244                     }
54245                     if(Roo.dd.DDM.locationCache[dd]){
54246                         delete Roo.dd.DDM.locationCache[dd];
54247                     }
54248                 }
54249                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54250             }
54251         }
54252         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54253         this.bind(null, null);
54254         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54255     },
54256
54257     handleLockChange : function(){
54258         this.refresh(true);
54259     },
54260
54261     onDenyColumnLock : function(){
54262
54263     },
54264
54265     onDenyColumnHide : function(){
54266
54267     },
54268
54269     handleHdMenuClick : function(item){
54270         var index = this.hdCtxIndex;
54271         var cm = this.cm, ds = this.ds;
54272         switch(item.id){
54273             case "asc":
54274                 ds.sort(cm.getDataIndex(index), "ASC");
54275                 break;
54276             case "desc":
54277                 ds.sort(cm.getDataIndex(index), "DESC");
54278                 break;
54279             case "lock":
54280                 var lc = cm.getLockedCount();
54281                 if(cm.getColumnCount(true) <= lc+1){
54282                     this.onDenyColumnLock();
54283                     return;
54284                 }
54285                 if(lc != index){
54286                     cm.setLocked(index, true, true);
54287                     cm.moveColumn(index, lc);
54288                     this.grid.fireEvent("columnmove", index, lc);
54289                 }else{
54290                     cm.setLocked(index, true);
54291                 }
54292             break;
54293             case "unlock":
54294                 var lc = cm.getLockedCount();
54295                 if((lc-1) != index){
54296                     cm.setLocked(index, false, true);
54297                     cm.moveColumn(index, lc-1);
54298                     this.grid.fireEvent("columnmove", index, lc-1);
54299                 }else{
54300                     cm.setLocked(index, false);
54301                 }
54302             break;
54303             case 'wider': // used to expand cols on touch..
54304             case 'narrow':
54305                 var cw = cm.getColumnWidth(index);
54306                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54307                 cw = Math.max(0, cw);
54308                 cw = Math.min(cw,4000);
54309                 cm.setColumnWidth(index, cw);
54310                 break;
54311                 
54312             default:
54313                 index = cm.getIndexById(item.id.substr(4));
54314                 if(index != -1){
54315                     if(item.checked && cm.getColumnCount(true) <= 1){
54316                         this.onDenyColumnHide();
54317                         return false;
54318                     }
54319                     cm.setHidden(index, item.checked);
54320                 }
54321         }
54322         return true;
54323     },
54324
54325     beforeColMenuShow : function(){
54326         var cm = this.cm,  colCount = cm.getColumnCount();
54327         this.colMenu.removeAll();
54328         for(var i = 0; i < colCount; i++){
54329             this.colMenu.add(new Roo.menu.CheckItem({
54330                 id: "col-"+cm.getColumnId(i),
54331                 text: cm.getColumnHeader(i),
54332                 checked: !cm.isHidden(i),
54333                 hideOnClick:false
54334             }));
54335         }
54336     },
54337
54338     handleHdCtx : function(g, index, e){
54339         e.stopEvent();
54340         var hd = this.getHeaderCell(index);
54341         this.hdCtxIndex = index;
54342         var ms = this.hmenu.items, cm = this.cm;
54343         ms.get("asc").setDisabled(!cm.isSortable(index));
54344         ms.get("desc").setDisabled(!cm.isSortable(index));
54345         if(this.grid.enableColLock !== false){
54346             ms.get("lock").setDisabled(cm.isLocked(index));
54347             ms.get("unlock").setDisabled(!cm.isLocked(index));
54348         }
54349         this.hmenu.show(hd, "tl-bl");
54350     },
54351
54352     handleHdOver : function(e){
54353         var hd = this.findHeaderCell(e.getTarget());
54354         if(hd && !this.headersDisabled){
54355             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54356                this.fly(hd).addClass("x-grid-hd-over");
54357             }
54358         }
54359     },
54360
54361     handleHdOut : function(e){
54362         var hd = this.findHeaderCell(e.getTarget());
54363         if(hd){
54364             this.fly(hd).removeClass("x-grid-hd-over");
54365         }
54366     },
54367
54368     handleSplitDblClick : function(e, t){
54369         var i = this.getCellIndex(t);
54370         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54371             this.autoSizeColumn(i, true);
54372             this.layout();
54373         }
54374     },
54375
54376     render : function(){
54377
54378         var cm = this.cm;
54379         var colCount = cm.getColumnCount();
54380
54381         if(this.grid.monitorWindowResize === true){
54382             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54383         }
54384         var header = this.renderHeaders();
54385         var body = this.templates.body.apply({rows:""});
54386         var html = this.templates.master.apply({
54387             lockedBody: body,
54388             body: body,
54389             lockedHeader: header[0],
54390             header: header[1]
54391         });
54392
54393         //this.updateColumns();
54394
54395         this.grid.getGridEl().dom.innerHTML = html;
54396
54397         this.initElements();
54398         
54399         // a kludge to fix the random scolling effect in webkit
54400         this.el.on("scroll", function() {
54401             this.el.dom.scrollTop=0; // hopefully not recursive..
54402         },this);
54403
54404         this.scroller.on("scroll", this.handleScroll, this);
54405         this.lockedBody.on("mousewheel", this.handleWheel, this);
54406         this.mainBody.on("mousewheel", this.handleWheel, this);
54407
54408         this.mainHd.on("mouseover", this.handleHdOver, this);
54409         this.mainHd.on("mouseout", this.handleHdOut, this);
54410         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54411                 {delegate: "."+this.splitClass});
54412
54413         this.lockedHd.on("mouseover", this.handleHdOver, this);
54414         this.lockedHd.on("mouseout", this.handleHdOut, this);
54415         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54416                 {delegate: "."+this.splitClass});
54417
54418         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54419             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54420         }
54421
54422         this.updateSplitters();
54423
54424         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54425             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54426             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54427         }
54428
54429         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54430             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54431             this.hmenu.add(
54432                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54433                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54434             );
54435             if(this.grid.enableColLock !== false){
54436                 this.hmenu.add('-',
54437                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54438                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54439                 );
54440             }
54441             if (Roo.isTouch) {
54442                  this.hmenu.add('-',
54443                     {id:"wider", text: this.columnsWiderText},
54444                     {id:"narrow", text: this.columnsNarrowText }
54445                 );
54446                 
54447                  
54448             }
54449             
54450             if(this.grid.enableColumnHide !== false){
54451
54452                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54453                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54454                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54455
54456                 this.hmenu.add('-',
54457                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54458                 );
54459             }
54460             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54461
54462             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54463         }
54464
54465         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54466             this.dd = new Roo.grid.GridDragZone(this.grid, {
54467                 ddGroup : this.grid.ddGroup || 'GridDD'
54468             });
54469             
54470         }
54471
54472         /*
54473         for(var i = 0; i < colCount; i++){
54474             if(cm.isHidden(i)){
54475                 this.hideColumn(i);
54476             }
54477             if(cm.config[i].align){
54478                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54479                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54480             }
54481         }*/
54482         
54483         this.updateHeaderSortState();
54484
54485         this.beforeInitialResize();
54486         this.layout(true);
54487
54488         // two part rendering gives faster view to the user
54489         this.renderPhase2.defer(1, this);
54490     },
54491
54492     renderPhase2 : function(){
54493         // render the rows now
54494         this.refresh();
54495         if(this.grid.autoSizeColumns){
54496             this.autoSizeColumns();
54497         }
54498     },
54499
54500     beforeInitialResize : function(){
54501
54502     },
54503
54504     onColumnSplitterMoved : function(i, w){
54505         this.userResized = true;
54506         var cm = this.grid.colModel;
54507         cm.setColumnWidth(i, w, true);
54508         var cid = cm.getColumnId(i);
54509         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54510         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54511         this.updateSplitters();
54512         this.layout();
54513         this.grid.fireEvent("columnresize", i, w);
54514     },
54515
54516     syncRowHeights : function(startIndex, endIndex){
54517         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54518             startIndex = startIndex || 0;
54519             var mrows = this.getBodyTable().rows;
54520             var lrows = this.getLockedTable().rows;
54521             var len = mrows.length-1;
54522             endIndex = Math.min(endIndex || len, len);
54523             for(var i = startIndex; i <= endIndex; i++){
54524                 var m = mrows[i], l = lrows[i];
54525                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54526                 m.style.height = l.style.height = h + "px";
54527             }
54528         }
54529     },
54530
54531     layout : function(initialRender, is2ndPass){
54532         var g = this.grid;
54533         var auto = g.autoHeight;
54534         var scrollOffset = 16;
54535         var c = g.getGridEl(), cm = this.cm,
54536                 expandCol = g.autoExpandColumn,
54537                 gv = this;
54538         //c.beginMeasure();
54539
54540         if(!c.dom.offsetWidth){ // display:none?
54541             if(initialRender){
54542                 this.lockedWrap.show();
54543                 this.mainWrap.show();
54544             }
54545             return;
54546         }
54547
54548         var hasLock = this.cm.isLocked(0);
54549
54550         var tbh = this.headerPanel.getHeight();
54551         var bbh = this.footerPanel.getHeight();
54552
54553         if(auto){
54554             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54555             var newHeight = ch + c.getBorderWidth("tb");
54556             if(g.maxHeight){
54557                 newHeight = Math.min(g.maxHeight, newHeight);
54558             }
54559             c.setHeight(newHeight);
54560         }
54561
54562         if(g.autoWidth){
54563             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54564         }
54565
54566         var s = this.scroller;
54567
54568         var csize = c.getSize(true);
54569
54570         this.el.setSize(csize.width, csize.height);
54571
54572         this.headerPanel.setWidth(csize.width);
54573         this.footerPanel.setWidth(csize.width);
54574
54575         var hdHeight = this.mainHd.getHeight();
54576         var vw = csize.width;
54577         var vh = csize.height - (tbh + bbh);
54578
54579         s.setSize(vw, vh);
54580
54581         var bt = this.getBodyTable();
54582         var ltWidth = hasLock ?
54583                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54584
54585         var scrollHeight = bt.offsetHeight;
54586         var scrollWidth = ltWidth + bt.offsetWidth;
54587         var vscroll = false, hscroll = false;
54588
54589         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54590
54591         var lw = this.lockedWrap, mw = this.mainWrap;
54592         var lb = this.lockedBody, mb = this.mainBody;
54593
54594         setTimeout(function(){
54595             var t = s.dom.offsetTop;
54596             var w = s.dom.clientWidth,
54597                 h = s.dom.clientHeight;
54598
54599             lw.setTop(t);
54600             lw.setSize(ltWidth, h);
54601
54602             mw.setLeftTop(ltWidth, t);
54603             mw.setSize(w-ltWidth, h);
54604
54605             lb.setHeight(h-hdHeight);
54606             mb.setHeight(h-hdHeight);
54607
54608             if(is2ndPass !== true && !gv.userResized && expandCol){
54609                 // high speed resize without full column calculation
54610                 
54611                 var ci = cm.getIndexById(expandCol);
54612                 if (ci < 0) {
54613                     ci = cm.findColumnIndex(expandCol);
54614                 }
54615                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54616                 var expandId = cm.getColumnId(ci);
54617                 var  tw = cm.getTotalWidth(false);
54618                 var currentWidth = cm.getColumnWidth(ci);
54619                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54620                 if(currentWidth != cw){
54621                     cm.setColumnWidth(ci, cw, true);
54622                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54623                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54624                     gv.updateSplitters();
54625                     gv.layout(false, true);
54626                 }
54627             }
54628
54629             if(initialRender){
54630                 lw.show();
54631                 mw.show();
54632             }
54633             //c.endMeasure();
54634         }, 10);
54635     },
54636
54637     onWindowResize : function(){
54638         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54639             return;
54640         }
54641         this.layout();
54642     },
54643
54644     appendFooter : function(parentEl){
54645         return null;
54646     },
54647
54648     sortAscText : "Sort Ascending",
54649     sortDescText : "Sort Descending",
54650     lockText : "Lock Column",
54651     unlockText : "Unlock Column",
54652     columnsText : "Columns",
54653  
54654     columnsWiderText : "Wider",
54655     columnsNarrowText : "Thinner"
54656 });
54657
54658
54659 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54660     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54661     this.proxy.el.addClass('x-grid3-col-dd');
54662 };
54663
54664 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54665     handleMouseDown : function(e){
54666
54667     },
54668
54669     callHandleMouseDown : function(e){
54670         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54671     }
54672 });
54673 /*
54674  * Based on:
54675  * Ext JS Library 1.1.1
54676  * Copyright(c) 2006-2007, Ext JS, LLC.
54677  *
54678  * Originally Released Under LGPL - original licence link has changed is not relivant.
54679  *
54680  * Fork - LGPL
54681  * <script type="text/javascript">
54682  */
54683  
54684 // private
54685 // This is a support class used internally by the Grid components
54686 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54687     this.grid = grid;
54688     this.view = grid.getView();
54689     this.proxy = this.view.resizeProxy;
54690     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54691         "gridSplitters" + this.grid.getGridEl().id, {
54692         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54693     });
54694     this.setHandleElId(Roo.id(hd));
54695     this.setOuterHandleElId(Roo.id(hd2));
54696     this.scroll = false;
54697 };
54698 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54699     fly: Roo.Element.fly,
54700
54701     b4StartDrag : function(x, y){
54702         this.view.headersDisabled = true;
54703         this.proxy.setHeight(this.view.mainWrap.getHeight());
54704         var w = this.cm.getColumnWidth(this.cellIndex);
54705         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54706         this.resetConstraints();
54707         this.setXConstraint(minw, 1000);
54708         this.setYConstraint(0, 0);
54709         this.minX = x - minw;
54710         this.maxX = x + 1000;
54711         this.startPos = x;
54712         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54713     },
54714
54715
54716     handleMouseDown : function(e){
54717         ev = Roo.EventObject.setEvent(e);
54718         var t = this.fly(ev.getTarget());
54719         if(t.hasClass("x-grid-split")){
54720             this.cellIndex = this.view.getCellIndex(t.dom);
54721             this.split = t.dom;
54722             this.cm = this.grid.colModel;
54723             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54724                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54725             }
54726         }
54727     },
54728
54729     endDrag : function(e){
54730         this.view.headersDisabled = false;
54731         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54732         var diff = endX - this.startPos;
54733         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54734     },
54735
54736     autoOffset : function(){
54737         this.setDelta(0,0);
54738     }
54739 });/*
54740  * Based on:
54741  * Ext JS Library 1.1.1
54742  * Copyright(c) 2006-2007, Ext JS, LLC.
54743  *
54744  * Originally Released Under LGPL - original licence link has changed is not relivant.
54745  *
54746  * Fork - LGPL
54747  * <script type="text/javascript">
54748  */
54749  
54750 // private
54751 // This is a support class used internally by the Grid components
54752 Roo.grid.GridDragZone = function(grid, config){
54753     this.view = grid.getView();
54754     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54755     if(this.view.lockedBody){
54756         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54757         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54758     }
54759     this.scroll = false;
54760     this.grid = grid;
54761     this.ddel = document.createElement('div');
54762     this.ddel.className = 'x-grid-dd-wrap';
54763 };
54764
54765 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54766     ddGroup : "GridDD",
54767
54768     getDragData : function(e){
54769         var t = Roo.lib.Event.getTarget(e);
54770         var rowIndex = this.view.findRowIndex(t);
54771         var sm = this.grid.selModel;
54772             
54773         //Roo.log(rowIndex);
54774         
54775         if (sm.getSelectedCell) {
54776             // cell selection..
54777             if (!sm.getSelectedCell()) {
54778                 return false;
54779             }
54780             if (rowIndex != sm.getSelectedCell()[0]) {
54781                 return false;
54782             }
54783         
54784         }
54785         
54786         if(rowIndex !== false){
54787             
54788             // if editorgrid.. 
54789             
54790             
54791             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54792                
54793             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54794               //  
54795             //}
54796             if (e.hasModifier()){
54797                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54798             }
54799             
54800             Roo.log("getDragData");
54801             
54802             return {
54803                 grid: this.grid,
54804                 ddel: this.ddel,
54805                 rowIndex: rowIndex,
54806                 selections:sm.getSelections ? sm.getSelections() : (
54807                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54808                 )
54809             };
54810         }
54811         return false;
54812     },
54813
54814     onInitDrag : function(e){
54815         var data = this.dragData;
54816         this.ddel.innerHTML = this.grid.getDragDropText();
54817         this.proxy.update(this.ddel);
54818         // fire start drag?
54819     },
54820
54821     afterRepair : function(){
54822         this.dragging = false;
54823     },
54824
54825     getRepairXY : function(e, data){
54826         return false;
54827     },
54828
54829     onEndDrag : function(data, e){
54830         // fire end drag?
54831     },
54832
54833     onValidDrop : function(dd, e, id){
54834         // fire drag drop?
54835         this.hideProxy();
54836     },
54837
54838     beforeInvalidDrop : function(e, id){
54839
54840     }
54841 });/*
54842  * Based on:
54843  * Ext JS Library 1.1.1
54844  * Copyright(c) 2006-2007, Ext JS, LLC.
54845  *
54846  * Originally Released Under LGPL - original licence link has changed is not relivant.
54847  *
54848  * Fork - LGPL
54849  * <script type="text/javascript">
54850  */
54851  
54852
54853 /**
54854  * @class Roo.grid.ColumnModel
54855  * @extends Roo.util.Observable
54856  * This is the default implementation of a ColumnModel used by the Grid. It defines
54857  * the columns in the grid.
54858  * <br>Usage:<br>
54859  <pre><code>
54860  var colModel = new Roo.grid.ColumnModel([
54861         {header: "Ticker", width: 60, sortable: true, locked: true},
54862         {header: "Company Name", width: 150, sortable: true},
54863         {header: "Market Cap.", width: 100, sortable: true},
54864         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54865         {header: "Employees", width: 100, sortable: true, resizable: false}
54866  ]);
54867  </code></pre>
54868  * <p>
54869  
54870  * The config options listed for this class are options which may appear in each
54871  * individual column definition.
54872  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54873  * @constructor
54874  * @param {Object} config An Array of column config objects. See this class's
54875  * config objects for details.
54876 */
54877 Roo.grid.ColumnModel = function(config){
54878         /**
54879      * The config passed into the constructor
54880      */
54881     this.config = config;
54882     this.lookup = {};
54883
54884     // if no id, create one
54885     // if the column does not have a dataIndex mapping,
54886     // map it to the order it is in the config
54887     for(var i = 0, len = config.length; i < len; i++){
54888         var c = config[i];
54889         if(typeof c.dataIndex == "undefined"){
54890             c.dataIndex = i;
54891         }
54892         if(typeof c.renderer == "string"){
54893             c.renderer = Roo.util.Format[c.renderer];
54894         }
54895         if(typeof c.id == "undefined"){
54896             c.id = Roo.id();
54897         }
54898         if(c.editor && c.editor.xtype){
54899             c.editor  = Roo.factory(c.editor, Roo.grid);
54900         }
54901         if(c.editor && c.editor.isFormField){
54902             c.editor = new Roo.grid.GridEditor(c.editor);
54903         }
54904         this.lookup[c.id] = c;
54905     }
54906
54907     /**
54908      * The width of columns which have no width specified (defaults to 100)
54909      * @type Number
54910      */
54911     this.defaultWidth = 100;
54912
54913     /**
54914      * Default sortable of columns which have no sortable specified (defaults to false)
54915      * @type Boolean
54916      */
54917     this.defaultSortable = false;
54918
54919     this.addEvents({
54920         /**
54921              * @event widthchange
54922              * Fires when the width of a column changes.
54923              * @param {ColumnModel} this
54924              * @param {Number} columnIndex The column index
54925              * @param {Number} newWidth The new width
54926              */
54927             "widthchange": true,
54928         /**
54929              * @event headerchange
54930              * Fires when the text of a header changes.
54931              * @param {ColumnModel} this
54932              * @param {Number} columnIndex The column index
54933              * @param {Number} newText The new header text
54934              */
54935             "headerchange": true,
54936         /**
54937              * @event hiddenchange
54938              * Fires when a column is hidden or "unhidden".
54939              * @param {ColumnModel} this
54940              * @param {Number} columnIndex The column index
54941              * @param {Boolean} hidden true if hidden, false otherwise
54942              */
54943             "hiddenchange": true,
54944             /**
54945          * @event columnmoved
54946          * Fires when a column is moved.
54947          * @param {ColumnModel} this
54948          * @param {Number} oldIndex
54949          * @param {Number} newIndex
54950          */
54951         "columnmoved" : true,
54952         /**
54953          * @event columlockchange
54954          * Fires when a column's locked state is changed
54955          * @param {ColumnModel} this
54956          * @param {Number} colIndex
54957          * @param {Boolean} locked true if locked
54958          */
54959         "columnlockchange" : true
54960     });
54961     Roo.grid.ColumnModel.superclass.constructor.call(this);
54962 };
54963 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54964     /**
54965      * @cfg {String} header The header text to display in the Grid view.
54966      */
54967     /**
54968      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54969      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54970      * specified, the column's index is used as an index into the Record's data Array.
54971      */
54972     /**
54973      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54974      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54975      */
54976     /**
54977      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54978      * Defaults to the value of the {@link #defaultSortable} property.
54979      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54980      */
54981     /**
54982      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54983      */
54984     /**
54985      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54986      */
54987     /**
54988      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54989      */
54990     /**
54991      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54992      */
54993     /**
54994      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54995      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54996      * default renderer uses the raw data value. If an object is returned (bootstrap only)
54997      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
54998      */
54999        /**
55000      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55001      */
55002     /**
55003      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55004      */
55005
55006     /**
55007      * Returns the id of the column at the specified index.
55008      * @param {Number} index The column index
55009      * @return {String} the id
55010      */
55011     getColumnId : function(index){
55012         return this.config[index].id;
55013     },
55014
55015     /**
55016      * Returns the column for a specified id.
55017      * @param {String} id The column id
55018      * @return {Object} the column
55019      */
55020     getColumnById : function(id){
55021         return this.lookup[id];
55022     },
55023
55024     
55025     /**
55026      * Returns the column for a specified dataIndex.
55027      * @param {String} dataIndex The column dataIndex
55028      * @return {Object|Boolean} the column or false if not found
55029      */
55030     getColumnByDataIndex: function(dataIndex){
55031         var index = this.findColumnIndex(dataIndex);
55032         return index > -1 ? this.config[index] : false;
55033     },
55034     
55035     /**
55036      * Returns the index for a specified column id.
55037      * @param {String} id The column id
55038      * @return {Number} the index, or -1 if not found
55039      */
55040     getIndexById : function(id){
55041         for(var i = 0, len = this.config.length; i < len; i++){
55042             if(this.config[i].id == id){
55043                 return i;
55044             }
55045         }
55046         return -1;
55047     },
55048     
55049     /**
55050      * Returns the index for a specified column dataIndex.
55051      * @param {String} dataIndex The column dataIndex
55052      * @return {Number} the index, or -1 if not found
55053      */
55054     
55055     findColumnIndex : function(dataIndex){
55056         for(var i = 0, len = this.config.length; i < len; i++){
55057             if(this.config[i].dataIndex == dataIndex){
55058                 return i;
55059             }
55060         }
55061         return -1;
55062     },
55063     
55064     
55065     moveColumn : function(oldIndex, newIndex){
55066         var c = this.config[oldIndex];
55067         this.config.splice(oldIndex, 1);
55068         this.config.splice(newIndex, 0, c);
55069         this.dataMap = null;
55070         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55071     },
55072
55073     isLocked : function(colIndex){
55074         return this.config[colIndex].locked === true;
55075     },
55076
55077     setLocked : function(colIndex, value, suppressEvent){
55078         if(this.isLocked(colIndex) == value){
55079             return;
55080         }
55081         this.config[colIndex].locked = value;
55082         if(!suppressEvent){
55083             this.fireEvent("columnlockchange", this, colIndex, value);
55084         }
55085     },
55086
55087     getTotalLockedWidth : function(){
55088         var totalWidth = 0;
55089         for(var i = 0; i < this.config.length; i++){
55090             if(this.isLocked(i) && !this.isHidden(i)){
55091                 this.totalWidth += this.getColumnWidth(i);
55092             }
55093         }
55094         return totalWidth;
55095     },
55096
55097     getLockedCount : function(){
55098         for(var i = 0, len = this.config.length; i < len; i++){
55099             if(!this.isLocked(i)){
55100                 return i;
55101             }
55102         }
55103     },
55104
55105     /**
55106      * Returns the number of columns.
55107      * @return {Number}
55108      */
55109     getColumnCount : function(visibleOnly){
55110         if(visibleOnly === true){
55111             var c = 0;
55112             for(var i = 0, len = this.config.length; i < len; i++){
55113                 if(!this.isHidden(i)){
55114                     c++;
55115                 }
55116             }
55117             return c;
55118         }
55119         return this.config.length;
55120     },
55121
55122     /**
55123      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55124      * @param {Function} fn
55125      * @param {Object} scope (optional)
55126      * @return {Array} result
55127      */
55128     getColumnsBy : function(fn, scope){
55129         var r = [];
55130         for(var i = 0, len = this.config.length; i < len; i++){
55131             var c = this.config[i];
55132             if(fn.call(scope||this, c, i) === true){
55133                 r[r.length] = c;
55134             }
55135         }
55136         return r;
55137     },
55138
55139     /**
55140      * Returns true if the specified column is sortable.
55141      * @param {Number} col The column index
55142      * @return {Boolean}
55143      */
55144     isSortable : function(col){
55145         if(typeof this.config[col].sortable == "undefined"){
55146             return this.defaultSortable;
55147         }
55148         return this.config[col].sortable;
55149     },
55150
55151     /**
55152      * Returns the rendering (formatting) function defined for the column.
55153      * @param {Number} col The column index.
55154      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55155      */
55156     getRenderer : function(col){
55157         if(!this.config[col].renderer){
55158             return Roo.grid.ColumnModel.defaultRenderer;
55159         }
55160         return this.config[col].renderer;
55161     },
55162
55163     /**
55164      * Sets the rendering (formatting) function for a column.
55165      * @param {Number} col The column index
55166      * @param {Function} fn The function to use to process the cell's raw data
55167      * to return HTML markup for the grid view. The render function is called with
55168      * the following parameters:<ul>
55169      * <li>Data value.</li>
55170      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55171      * <li>css A CSS style string to apply to the table cell.</li>
55172      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55173      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55174      * <li>Row index</li>
55175      * <li>Column index</li>
55176      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55177      */
55178     setRenderer : function(col, fn){
55179         this.config[col].renderer = fn;
55180     },
55181
55182     /**
55183      * Returns the width for the specified column.
55184      * @param {Number} col The column index
55185      * @return {Number}
55186      */
55187     getColumnWidth : function(col){
55188         return this.config[col].width * 1 || this.defaultWidth;
55189     },
55190
55191     /**
55192      * Sets the width for a column.
55193      * @param {Number} col The column index
55194      * @param {Number} width The new width
55195      */
55196     setColumnWidth : function(col, width, suppressEvent){
55197         this.config[col].width = width;
55198         this.totalWidth = null;
55199         if(!suppressEvent){
55200              this.fireEvent("widthchange", this, col, width);
55201         }
55202     },
55203
55204     /**
55205      * Returns the total width of all columns.
55206      * @param {Boolean} includeHidden True to include hidden column widths
55207      * @return {Number}
55208      */
55209     getTotalWidth : function(includeHidden){
55210         if(!this.totalWidth){
55211             this.totalWidth = 0;
55212             for(var i = 0, len = this.config.length; i < len; i++){
55213                 if(includeHidden || !this.isHidden(i)){
55214                     this.totalWidth += this.getColumnWidth(i);
55215                 }
55216             }
55217         }
55218         return this.totalWidth;
55219     },
55220
55221     /**
55222      * Returns the header for the specified column.
55223      * @param {Number} col The column index
55224      * @return {String}
55225      */
55226     getColumnHeader : function(col){
55227         return this.config[col].header;
55228     },
55229
55230     /**
55231      * Sets the header for a column.
55232      * @param {Number} col The column index
55233      * @param {String} header The new header
55234      */
55235     setColumnHeader : function(col, header){
55236         this.config[col].header = header;
55237         this.fireEvent("headerchange", this, col, header);
55238     },
55239
55240     /**
55241      * Returns the tooltip for the specified column.
55242      * @param {Number} col The column index
55243      * @return {String}
55244      */
55245     getColumnTooltip : function(col){
55246             return this.config[col].tooltip;
55247     },
55248     /**
55249      * Sets the tooltip for a column.
55250      * @param {Number} col The column index
55251      * @param {String} tooltip The new tooltip
55252      */
55253     setColumnTooltip : function(col, tooltip){
55254             this.config[col].tooltip = tooltip;
55255     },
55256
55257     /**
55258      * Returns the dataIndex for the specified column.
55259      * @param {Number} col The column index
55260      * @return {Number}
55261      */
55262     getDataIndex : function(col){
55263         return this.config[col].dataIndex;
55264     },
55265
55266     /**
55267      * Sets the dataIndex for a column.
55268      * @param {Number} col The column index
55269      * @param {Number} dataIndex The new dataIndex
55270      */
55271     setDataIndex : function(col, dataIndex){
55272         this.config[col].dataIndex = dataIndex;
55273     },
55274
55275     
55276     
55277     /**
55278      * Returns true if the cell is editable.
55279      * @param {Number} colIndex The column index
55280      * @param {Number} rowIndex The row index
55281      * @return {Boolean}
55282      */
55283     isCellEditable : function(colIndex, rowIndex){
55284         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55285     },
55286
55287     /**
55288      * Returns the editor defined for the cell/column.
55289      * return false or null to disable editing.
55290      * @param {Number} colIndex The column index
55291      * @param {Number} rowIndex The row index
55292      * @return {Object}
55293      */
55294     getCellEditor : function(colIndex, rowIndex){
55295         return this.config[colIndex].editor;
55296     },
55297
55298     /**
55299      * Sets if a column is editable.
55300      * @param {Number} col The column index
55301      * @param {Boolean} editable True if the column is editable
55302      */
55303     setEditable : function(col, editable){
55304         this.config[col].editable = editable;
55305     },
55306
55307
55308     /**
55309      * Returns true if the column is hidden.
55310      * @param {Number} colIndex The column index
55311      * @return {Boolean}
55312      */
55313     isHidden : function(colIndex){
55314         return this.config[colIndex].hidden;
55315     },
55316
55317
55318     /**
55319      * Returns true if the column width cannot be changed
55320      */
55321     isFixed : function(colIndex){
55322         return this.config[colIndex].fixed;
55323     },
55324
55325     /**
55326      * Returns true if the column can be resized
55327      * @return {Boolean}
55328      */
55329     isResizable : function(colIndex){
55330         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55331     },
55332     /**
55333      * Sets if a column is hidden.
55334      * @param {Number} colIndex The column index
55335      * @param {Boolean} hidden True if the column is hidden
55336      */
55337     setHidden : function(colIndex, hidden){
55338         this.config[colIndex].hidden = hidden;
55339         this.totalWidth = null;
55340         this.fireEvent("hiddenchange", this, colIndex, hidden);
55341     },
55342
55343     /**
55344      * Sets the editor for a column.
55345      * @param {Number} col The column index
55346      * @param {Object} editor The editor object
55347      */
55348     setEditor : function(col, editor){
55349         this.config[col].editor = editor;
55350     }
55351 });
55352
55353 Roo.grid.ColumnModel.defaultRenderer = function(value){
55354         if(typeof value == "string" && value.length < 1){
55355             return "&#160;";
55356         }
55357         return value;
55358 };
55359
55360 // Alias for backwards compatibility
55361 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55362 /*
55363  * Based on:
55364  * Ext JS Library 1.1.1
55365  * Copyright(c) 2006-2007, Ext JS, LLC.
55366  *
55367  * Originally Released Under LGPL - original licence link has changed is not relivant.
55368  *
55369  * Fork - LGPL
55370  * <script type="text/javascript">
55371  */
55372
55373 /**
55374  * @class Roo.grid.AbstractSelectionModel
55375  * @extends Roo.util.Observable
55376  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55377  * implemented by descendant classes.  This class should not be directly instantiated.
55378  * @constructor
55379  */
55380 Roo.grid.AbstractSelectionModel = function(){
55381     this.locked = false;
55382     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55383 };
55384
55385 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55386     /** @ignore Called by the grid automatically. Do not call directly. */
55387     init : function(grid){
55388         this.grid = grid;
55389         this.initEvents();
55390     },
55391
55392     /**
55393      * Locks the selections.
55394      */
55395     lock : function(){
55396         this.locked = true;
55397     },
55398
55399     /**
55400      * Unlocks the selections.
55401      */
55402     unlock : function(){
55403         this.locked = false;
55404     },
55405
55406     /**
55407      * Returns true if the selections are locked.
55408      * @return {Boolean}
55409      */
55410     isLocked : function(){
55411         return this.locked;
55412     }
55413 });/*
55414  * Based on:
55415  * Ext JS Library 1.1.1
55416  * Copyright(c) 2006-2007, Ext JS, LLC.
55417  *
55418  * Originally Released Under LGPL - original licence link has changed is not relivant.
55419  *
55420  * Fork - LGPL
55421  * <script type="text/javascript">
55422  */
55423 /**
55424  * @extends Roo.grid.AbstractSelectionModel
55425  * @class Roo.grid.RowSelectionModel
55426  * The default SelectionModel used by {@link Roo.grid.Grid}.
55427  * It supports multiple selections and keyboard selection/navigation. 
55428  * @constructor
55429  * @param {Object} config
55430  */
55431 Roo.grid.RowSelectionModel = function(config){
55432     Roo.apply(this, config);
55433     this.selections = new Roo.util.MixedCollection(false, function(o){
55434         return o.id;
55435     });
55436
55437     this.last = false;
55438     this.lastActive = false;
55439
55440     this.addEvents({
55441         /**
55442              * @event selectionchange
55443              * Fires when the selection changes
55444              * @param {SelectionModel} this
55445              */
55446             "selectionchange" : true,
55447         /**
55448              * @event afterselectionchange
55449              * Fires after the selection changes (eg. by key press or clicking)
55450              * @param {SelectionModel} this
55451              */
55452             "afterselectionchange" : true,
55453         /**
55454              * @event beforerowselect
55455              * Fires when a row is selected being selected, return false to cancel.
55456              * @param {SelectionModel} this
55457              * @param {Number} rowIndex The selected index
55458              * @param {Boolean} keepExisting False if other selections will be cleared
55459              */
55460             "beforerowselect" : true,
55461         /**
55462              * @event rowselect
55463              * Fires when a row is selected.
55464              * @param {SelectionModel} this
55465              * @param {Number} rowIndex The selected index
55466              * @param {Roo.data.Record} r The record
55467              */
55468             "rowselect" : true,
55469         /**
55470              * @event rowdeselect
55471              * Fires when a row is deselected.
55472              * @param {SelectionModel} this
55473              * @param {Number} rowIndex The selected index
55474              */
55475         "rowdeselect" : true
55476     });
55477     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55478     this.locked = false;
55479 };
55480
55481 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55482     /**
55483      * @cfg {Boolean} singleSelect
55484      * True to allow selection of only one row at a time (defaults to false)
55485      */
55486     singleSelect : false,
55487
55488     // private
55489     initEvents : function(){
55490
55491         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55492             this.grid.on("mousedown", this.handleMouseDown, this);
55493         }else{ // allow click to work like normal
55494             this.grid.on("rowclick", this.handleDragableRowClick, this);
55495         }
55496
55497         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55498             "up" : function(e){
55499                 if(!e.shiftKey){
55500                     this.selectPrevious(e.shiftKey);
55501                 }else if(this.last !== false && this.lastActive !== false){
55502                     var last = this.last;
55503                     this.selectRange(this.last,  this.lastActive-1);
55504                     this.grid.getView().focusRow(this.lastActive);
55505                     if(last !== false){
55506                         this.last = last;
55507                     }
55508                 }else{
55509                     this.selectFirstRow();
55510                 }
55511                 this.fireEvent("afterselectionchange", this);
55512             },
55513             "down" : function(e){
55514                 if(!e.shiftKey){
55515                     this.selectNext(e.shiftKey);
55516                 }else if(this.last !== false && this.lastActive !== false){
55517                     var last = this.last;
55518                     this.selectRange(this.last,  this.lastActive+1);
55519                     this.grid.getView().focusRow(this.lastActive);
55520                     if(last !== false){
55521                         this.last = last;
55522                     }
55523                 }else{
55524                     this.selectFirstRow();
55525                 }
55526                 this.fireEvent("afterselectionchange", this);
55527             },
55528             scope: this
55529         });
55530
55531         var view = this.grid.view;
55532         view.on("refresh", this.onRefresh, this);
55533         view.on("rowupdated", this.onRowUpdated, this);
55534         view.on("rowremoved", this.onRemove, this);
55535     },
55536
55537     // private
55538     onRefresh : function(){
55539         var ds = this.grid.dataSource, i, v = this.grid.view;
55540         var s = this.selections;
55541         s.each(function(r){
55542             if((i = ds.indexOfId(r.id)) != -1){
55543                 v.onRowSelect(i);
55544             }else{
55545                 s.remove(r);
55546             }
55547         });
55548     },
55549
55550     // private
55551     onRemove : function(v, index, r){
55552         this.selections.remove(r);
55553     },
55554
55555     // private
55556     onRowUpdated : function(v, index, r){
55557         if(this.isSelected(r)){
55558             v.onRowSelect(index);
55559         }
55560     },
55561
55562     /**
55563      * Select records.
55564      * @param {Array} records The records to select
55565      * @param {Boolean} keepExisting (optional) True to keep existing selections
55566      */
55567     selectRecords : function(records, keepExisting){
55568         if(!keepExisting){
55569             this.clearSelections();
55570         }
55571         var ds = this.grid.dataSource;
55572         for(var i = 0, len = records.length; i < len; i++){
55573             this.selectRow(ds.indexOf(records[i]), true);
55574         }
55575     },
55576
55577     /**
55578      * Gets the number of selected rows.
55579      * @return {Number}
55580      */
55581     getCount : function(){
55582         return this.selections.length;
55583     },
55584
55585     /**
55586      * Selects the first row in the grid.
55587      */
55588     selectFirstRow : function(){
55589         this.selectRow(0);
55590     },
55591
55592     /**
55593      * Select the last row.
55594      * @param {Boolean} keepExisting (optional) True to keep existing selections
55595      */
55596     selectLastRow : function(keepExisting){
55597         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55598     },
55599
55600     /**
55601      * Selects the row immediately following the last selected row.
55602      * @param {Boolean} keepExisting (optional) True to keep existing selections
55603      */
55604     selectNext : function(keepExisting){
55605         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55606             this.selectRow(this.last+1, keepExisting);
55607             this.grid.getView().focusRow(this.last);
55608         }
55609     },
55610
55611     /**
55612      * Selects the row that precedes the last selected row.
55613      * @param {Boolean} keepExisting (optional) True to keep existing selections
55614      */
55615     selectPrevious : function(keepExisting){
55616         if(this.last){
55617             this.selectRow(this.last-1, keepExisting);
55618             this.grid.getView().focusRow(this.last);
55619         }
55620     },
55621
55622     /**
55623      * Returns the selected records
55624      * @return {Array} Array of selected records
55625      */
55626     getSelections : function(){
55627         return [].concat(this.selections.items);
55628     },
55629
55630     /**
55631      * Returns the first selected record.
55632      * @return {Record}
55633      */
55634     getSelected : function(){
55635         return this.selections.itemAt(0);
55636     },
55637
55638
55639     /**
55640      * Clears all selections.
55641      */
55642     clearSelections : function(fast){
55643         if(this.locked) return;
55644         if(fast !== true){
55645             var ds = this.grid.dataSource;
55646             var s = this.selections;
55647             s.each(function(r){
55648                 this.deselectRow(ds.indexOfId(r.id));
55649             }, this);
55650             s.clear();
55651         }else{
55652             this.selections.clear();
55653         }
55654         this.last = false;
55655     },
55656
55657
55658     /**
55659      * Selects all rows.
55660      */
55661     selectAll : function(){
55662         if(this.locked) return;
55663         this.selections.clear();
55664         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55665             this.selectRow(i, true);
55666         }
55667     },
55668
55669     /**
55670      * Returns True if there is a selection.
55671      * @return {Boolean}
55672      */
55673     hasSelection : function(){
55674         return this.selections.length > 0;
55675     },
55676
55677     /**
55678      * Returns True if the specified row is selected.
55679      * @param {Number/Record} record The record or index of the record to check
55680      * @return {Boolean}
55681      */
55682     isSelected : function(index){
55683         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55684         return (r && this.selections.key(r.id) ? true : false);
55685     },
55686
55687     /**
55688      * Returns True if the specified record id is selected.
55689      * @param {String} id The id of record to check
55690      * @return {Boolean}
55691      */
55692     isIdSelected : function(id){
55693         return (this.selections.key(id) ? true : false);
55694     },
55695
55696     // private
55697     handleMouseDown : function(e, t){
55698         var view = this.grid.getView(), rowIndex;
55699         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55700             return;
55701         };
55702         if(e.shiftKey && this.last !== false){
55703             var last = this.last;
55704             this.selectRange(last, rowIndex, e.ctrlKey);
55705             this.last = last; // reset the last
55706             view.focusRow(rowIndex);
55707         }else{
55708             var isSelected = this.isSelected(rowIndex);
55709             if(e.button !== 0 && isSelected){
55710                 view.focusRow(rowIndex);
55711             }else if(e.ctrlKey && isSelected){
55712                 this.deselectRow(rowIndex);
55713             }else if(!isSelected){
55714                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55715                 view.focusRow(rowIndex);
55716             }
55717         }
55718         this.fireEvent("afterselectionchange", this);
55719     },
55720     // private
55721     handleDragableRowClick :  function(grid, rowIndex, e) 
55722     {
55723         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55724             this.selectRow(rowIndex, false);
55725             grid.view.focusRow(rowIndex);
55726              this.fireEvent("afterselectionchange", this);
55727         }
55728     },
55729     
55730     /**
55731      * Selects multiple rows.
55732      * @param {Array} rows Array of the indexes of the row to select
55733      * @param {Boolean} keepExisting (optional) True to keep existing selections
55734      */
55735     selectRows : function(rows, keepExisting){
55736         if(!keepExisting){
55737             this.clearSelections();
55738         }
55739         for(var i = 0, len = rows.length; i < len; i++){
55740             this.selectRow(rows[i], true);
55741         }
55742     },
55743
55744     /**
55745      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55746      * @param {Number} startRow The index of the first row in the range
55747      * @param {Number} endRow The index of the last row in the range
55748      * @param {Boolean} keepExisting (optional) True to retain existing selections
55749      */
55750     selectRange : function(startRow, endRow, keepExisting){
55751         if(this.locked) return;
55752         if(!keepExisting){
55753             this.clearSelections();
55754         }
55755         if(startRow <= endRow){
55756             for(var i = startRow; i <= endRow; i++){
55757                 this.selectRow(i, true);
55758             }
55759         }else{
55760             for(var i = startRow; i >= endRow; i--){
55761                 this.selectRow(i, true);
55762             }
55763         }
55764     },
55765
55766     /**
55767      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55768      * @param {Number} startRow The index of the first row in the range
55769      * @param {Number} endRow The index of the last row in the range
55770      */
55771     deselectRange : function(startRow, endRow, preventViewNotify){
55772         if(this.locked) return;
55773         for(var i = startRow; i <= endRow; i++){
55774             this.deselectRow(i, preventViewNotify);
55775         }
55776     },
55777
55778     /**
55779      * Selects a row.
55780      * @param {Number} row The index of the row to select
55781      * @param {Boolean} keepExisting (optional) True to keep existing selections
55782      */
55783     selectRow : function(index, keepExisting, preventViewNotify){
55784         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55785         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55786             if(!keepExisting || this.singleSelect){
55787                 this.clearSelections();
55788             }
55789             var r = this.grid.dataSource.getAt(index);
55790             this.selections.add(r);
55791             this.last = this.lastActive = index;
55792             if(!preventViewNotify){
55793                 this.grid.getView().onRowSelect(index);
55794             }
55795             this.fireEvent("rowselect", this, index, r);
55796             this.fireEvent("selectionchange", this);
55797         }
55798     },
55799
55800     /**
55801      * Deselects a row.
55802      * @param {Number} row The index of the row to deselect
55803      */
55804     deselectRow : function(index, preventViewNotify){
55805         if(this.locked) return;
55806         if(this.last == index){
55807             this.last = false;
55808         }
55809         if(this.lastActive == index){
55810             this.lastActive = false;
55811         }
55812         var r = this.grid.dataSource.getAt(index);
55813         this.selections.remove(r);
55814         if(!preventViewNotify){
55815             this.grid.getView().onRowDeselect(index);
55816         }
55817         this.fireEvent("rowdeselect", this, index);
55818         this.fireEvent("selectionchange", this);
55819     },
55820
55821     // private
55822     restoreLast : function(){
55823         if(this._last){
55824             this.last = this._last;
55825         }
55826     },
55827
55828     // private
55829     acceptsNav : function(row, col, cm){
55830         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55831     },
55832
55833     // private
55834     onEditorKey : function(field, e){
55835         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55836         if(k == e.TAB){
55837             e.stopEvent();
55838             ed.completeEdit();
55839             if(e.shiftKey){
55840                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55841             }else{
55842                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55843             }
55844         }else if(k == e.ENTER && !e.ctrlKey){
55845             e.stopEvent();
55846             ed.completeEdit();
55847             if(e.shiftKey){
55848                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55849             }else{
55850                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55851             }
55852         }else if(k == e.ESC){
55853             ed.cancelEdit();
55854         }
55855         if(newCell){
55856             g.startEditing(newCell[0], newCell[1]);
55857         }
55858     }
55859 });/*
55860  * Based on:
55861  * Ext JS Library 1.1.1
55862  * Copyright(c) 2006-2007, Ext JS, LLC.
55863  *
55864  * Originally Released Under LGPL - original licence link has changed is not relivant.
55865  *
55866  * Fork - LGPL
55867  * <script type="text/javascript">
55868  */
55869 /**
55870  * @class Roo.grid.CellSelectionModel
55871  * @extends Roo.grid.AbstractSelectionModel
55872  * This class provides the basic implementation for cell selection in a grid.
55873  * @constructor
55874  * @param {Object} config The object containing the configuration of this model.
55875  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55876  */
55877 Roo.grid.CellSelectionModel = function(config){
55878     Roo.apply(this, config);
55879
55880     this.selection = null;
55881
55882     this.addEvents({
55883         /**
55884              * @event beforerowselect
55885              * Fires before a cell is selected.
55886              * @param {SelectionModel} this
55887              * @param {Number} rowIndex The selected row index
55888              * @param {Number} colIndex The selected cell index
55889              */
55890             "beforecellselect" : true,
55891         /**
55892              * @event cellselect
55893              * Fires when a cell is selected.
55894              * @param {SelectionModel} this
55895              * @param {Number} rowIndex The selected row index
55896              * @param {Number} colIndex The selected cell index
55897              */
55898             "cellselect" : true,
55899         /**
55900              * @event selectionchange
55901              * Fires when the active selection changes.
55902              * @param {SelectionModel} this
55903              * @param {Object} selection null for no selection or an object (o) with two properties
55904                 <ul>
55905                 <li>o.record: the record object for the row the selection is in</li>
55906                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55907                 </ul>
55908              */
55909             "selectionchange" : true,
55910         /**
55911              * @event tabend
55912              * Fires when the tab (or enter) was pressed on the last editable cell
55913              * You can use this to trigger add new row.
55914              * @param {SelectionModel} this
55915              */
55916             "tabend" : true,
55917          /**
55918              * @event beforeeditnext
55919              * Fires before the next editable sell is made active
55920              * You can use this to skip to another cell or fire the tabend
55921              *    if you set cell to false
55922              * @param {Object} eventdata object : { cell : [ row, col ] } 
55923              */
55924             "beforeeditnext" : true
55925     });
55926     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55927 };
55928
55929 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55930     
55931     enter_is_tab: false,
55932
55933     /** @ignore */
55934     initEvents : function(){
55935         this.grid.on("mousedown", this.handleMouseDown, this);
55936         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55937         var view = this.grid.view;
55938         view.on("refresh", this.onViewChange, this);
55939         view.on("rowupdated", this.onRowUpdated, this);
55940         view.on("beforerowremoved", this.clearSelections, this);
55941         view.on("beforerowsinserted", this.clearSelections, this);
55942         if(this.grid.isEditor){
55943             this.grid.on("beforeedit", this.beforeEdit,  this);
55944         }
55945     },
55946
55947         //private
55948     beforeEdit : function(e){
55949         this.select(e.row, e.column, false, true, e.record);
55950     },
55951
55952         //private
55953     onRowUpdated : function(v, index, r){
55954         if(this.selection && this.selection.record == r){
55955             v.onCellSelect(index, this.selection.cell[1]);
55956         }
55957     },
55958
55959         //private
55960     onViewChange : function(){
55961         this.clearSelections(true);
55962     },
55963
55964         /**
55965          * Returns the currently selected cell,.
55966          * @return {Array} The selected cell (row, column) or null if none selected.
55967          */
55968     getSelectedCell : function(){
55969         return this.selection ? this.selection.cell : null;
55970     },
55971
55972     /**
55973      * Clears all selections.
55974      * @param {Boolean} true to prevent the gridview from being notified about the change.
55975      */
55976     clearSelections : function(preventNotify){
55977         var s = this.selection;
55978         if(s){
55979             if(preventNotify !== true){
55980                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55981             }
55982             this.selection = null;
55983             this.fireEvent("selectionchange", this, null);
55984         }
55985     },
55986
55987     /**
55988      * Returns true if there is a selection.
55989      * @return {Boolean}
55990      */
55991     hasSelection : function(){
55992         return this.selection ? true : false;
55993     },
55994
55995     /** @ignore */
55996     handleMouseDown : function(e, t){
55997         var v = this.grid.getView();
55998         if(this.isLocked()){
55999             return;
56000         };
56001         var row = v.findRowIndex(t);
56002         var cell = v.findCellIndex(t);
56003         if(row !== false && cell !== false){
56004             this.select(row, cell);
56005         }
56006     },
56007
56008     /**
56009      * Selects a cell.
56010      * @param {Number} rowIndex
56011      * @param {Number} collIndex
56012      */
56013     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56014         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56015             this.clearSelections();
56016             r = r || this.grid.dataSource.getAt(rowIndex);
56017             this.selection = {
56018                 record : r,
56019                 cell : [rowIndex, colIndex]
56020             };
56021             if(!preventViewNotify){
56022                 var v = this.grid.getView();
56023                 v.onCellSelect(rowIndex, colIndex);
56024                 if(preventFocus !== true){
56025                     v.focusCell(rowIndex, colIndex);
56026                 }
56027             }
56028             this.fireEvent("cellselect", this, rowIndex, colIndex);
56029             this.fireEvent("selectionchange", this, this.selection);
56030         }
56031     },
56032
56033         //private
56034     isSelectable : function(rowIndex, colIndex, cm){
56035         return !cm.isHidden(colIndex);
56036     },
56037
56038     /** @ignore */
56039     handleKeyDown : function(e){
56040         //Roo.log('Cell Sel Model handleKeyDown');
56041         if(!e.isNavKeyPress()){
56042             return;
56043         }
56044         var g = this.grid, s = this.selection;
56045         if(!s){
56046             e.stopEvent();
56047             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56048             if(cell){
56049                 this.select(cell[0], cell[1]);
56050             }
56051             return;
56052         }
56053         var sm = this;
56054         var walk = function(row, col, step){
56055             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56056         };
56057         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56058         var newCell;
56059
56060       
56061
56062         switch(k){
56063             case e.TAB:
56064                 // handled by onEditorKey
56065                 if (g.isEditor && g.editing) {
56066                     return;
56067                 }
56068                 if(e.shiftKey) {
56069                     newCell = walk(r, c-1, -1);
56070                 } else {
56071                     newCell = walk(r, c+1, 1);
56072                 }
56073                 break;
56074             
56075             case e.DOWN:
56076                newCell = walk(r+1, c, 1);
56077                 break;
56078             
56079             case e.UP:
56080                 newCell = walk(r-1, c, -1);
56081                 break;
56082             
56083             case e.RIGHT:
56084                 newCell = walk(r, c+1, 1);
56085                 break;
56086             
56087             case e.LEFT:
56088                 newCell = walk(r, c-1, -1);
56089                 break;
56090             
56091             case e.ENTER:
56092                 
56093                 if(g.isEditor && !g.editing){
56094                    g.startEditing(r, c);
56095                    e.stopEvent();
56096                    return;
56097                 }
56098                 
56099                 
56100              break;
56101         };
56102         if(newCell){
56103             this.select(newCell[0], newCell[1]);
56104             e.stopEvent();
56105             
56106         }
56107     },
56108
56109     acceptsNav : function(row, col, cm){
56110         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56111     },
56112     /**
56113      * Selects a cell.
56114      * @param {Number} field (not used) - as it's normally used as a listener
56115      * @param {Number} e - event - fake it by using
56116      *
56117      * var e = Roo.EventObjectImpl.prototype;
56118      * e.keyCode = e.TAB
56119      *
56120      * 
56121      */
56122     onEditorKey : function(field, e){
56123         
56124         var k = e.getKey(),
56125             newCell,
56126             g = this.grid,
56127             ed = g.activeEditor,
56128             forward = false;
56129         ///Roo.log('onEditorKey' + k);
56130         
56131         
56132         if (this.enter_is_tab && k == e.ENTER) {
56133             k = e.TAB;
56134         }
56135         
56136         if(k == e.TAB){
56137             if(e.shiftKey){
56138                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56139             }else{
56140                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56141                 forward = true;
56142             }
56143             
56144             e.stopEvent();
56145             
56146         } else if(k == e.ENTER &&  !e.ctrlKey){
56147             ed.completeEdit();
56148             e.stopEvent();
56149             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56150         
56151                 } else if(k == e.ESC){
56152             ed.cancelEdit();
56153         }
56154                 
56155         if (newCell) {
56156             var ecall = { cell : newCell, forward : forward };
56157             this.fireEvent('beforeeditnext', ecall );
56158             newCell = ecall.cell;
56159                         forward = ecall.forward;
56160         }
56161                 
56162         if(newCell){
56163             //Roo.log('next cell after edit');
56164             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56165         } else if (forward) {
56166             // tabbed past last
56167             this.fireEvent.defer(100, this, ['tabend',this]);
56168         }
56169     }
56170 });/*
56171  * Based on:
56172  * Ext JS Library 1.1.1
56173  * Copyright(c) 2006-2007, Ext JS, LLC.
56174  *
56175  * Originally Released Under LGPL - original licence link has changed is not relivant.
56176  *
56177  * Fork - LGPL
56178  * <script type="text/javascript">
56179  */
56180  
56181 /**
56182  * @class Roo.grid.EditorGrid
56183  * @extends Roo.grid.Grid
56184  * Class for creating and editable grid.
56185  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56186  * The container MUST have some type of size defined for the grid to fill. The container will be 
56187  * automatically set to position relative if it isn't already.
56188  * @param {Object} dataSource The data model to bind to
56189  * @param {Object} colModel The column model with info about this grid's columns
56190  */
56191 Roo.grid.EditorGrid = function(container, config){
56192     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56193     this.getGridEl().addClass("xedit-grid");
56194
56195     if(!this.selModel){
56196         this.selModel = new Roo.grid.CellSelectionModel();
56197     }
56198
56199     this.activeEditor = null;
56200
56201         this.addEvents({
56202             /**
56203              * @event beforeedit
56204              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56205              * <ul style="padding:5px;padding-left:16px;">
56206              * <li>grid - This grid</li>
56207              * <li>record - The record being edited</li>
56208              * <li>field - The field name being edited</li>
56209              * <li>value - The value for the field being edited.</li>
56210              * <li>row - The grid row index</li>
56211              * <li>column - The grid column index</li>
56212              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56213              * </ul>
56214              * @param {Object} e An edit event (see above for description)
56215              */
56216             "beforeedit" : true,
56217             /**
56218              * @event afteredit
56219              * Fires after a cell is edited. <br />
56220              * <ul style="padding:5px;padding-left:16px;">
56221              * <li>grid - This grid</li>
56222              * <li>record - The record being edited</li>
56223              * <li>field - The field name being edited</li>
56224              * <li>value - The value being set</li>
56225              * <li>originalValue - The original value for the field, before the edit.</li>
56226              * <li>row - The grid row index</li>
56227              * <li>column - The grid column index</li>
56228              * </ul>
56229              * @param {Object} e An edit event (see above for description)
56230              */
56231             "afteredit" : true,
56232             /**
56233              * @event validateedit
56234              * Fires after a cell is edited, but before the value is set in the record. 
56235          * You can use this to modify the value being set in the field, Return false
56236              * to cancel the change. The edit event object has the following properties <br />
56237              * <ul style="padding:5px;padding-left:16px;">
56238          * <li>editor - This editor</li>
56239              * <li>grid - This grid</li>
56240              * <li>record - The record being edited</li>
56241              * <li>field - The field name being edited</li>
56242              * <li>value - The value being set</li>
56243              * <li>originalValue - The original value for the field, before the edit.</li>
56244              * <li>row - The grid row index</li>
56245              * <li>column - The grid column index</li>
56246              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56247              * </ul>
56248              * @param {Object} e An edit event (see above for description)
56249              */
56250             "validateedit" : true
56251         });
56252     this.on("bodyscroll", this.stopEditing,  this);
56253     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56254 };
56255
56256 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56257     /**
56258      * @cfg {Number} clicksToEdit
56259      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56260      */
56261     clicksToEdit: 2,
56262
56263     // private
56264     isEditor : true,
56265     // private
56266     trackMouseOver: false, // causes very odd FF errors
56267
56268     onCellDblClick : function(g, row, col){
56269         this.startEditing(row, col);
56270     },
56271
56272     onEditComplete : function(ed, value, startValue){
56273         this.editing = false;
56274         this.activeEditor = null;
56275         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56276         var r = ed.record;
56277         var field = this.colModel.getDataIndex(ed.col);
56278         var e = {
56279             grid: this,
56280             record: r,
56281             field: field,
56282             originalValue: startValue,
56283             value: value,
56284             row: ed.row,
56285             column: ed.col,
56286             cancel:false,
56287             editor: ed
56288         };
56289         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56290         cell.show();
56291           
56292         if(String(value) !== String(startValue)){
56293             
56294             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56295                 r.set(field, e.value);
56296                 // if we are dealing with a combo box..
56297                 // then we also set the 'name' colum to be the displayField
56298                 if (ed.field.displayField && ed.field.name) {
56299                     r.set(ed.field.name, ed.field.el.dom.value);
56300                 }
56301                 
56302                 delete e.cancel; //?? why!!!
56303                 this.fireEvent("afteredit", e);
56304             }
56305         } else {
56306             this.fireEvent("afteredit", e); // always fire it!
56307         }
56308         this.view.focusCell(ed.row, ed.col);
56309     },
56310
56311     /**
56312      * Starts editing the specified for the specified row/column
56313      * @param {Number} rowIndex
56314      * @param {Number} colIndex
56315      */
56316     startEditing : function(row, col){
56317         this.stopEditing();
56318         if(this.colModel.isCellEditable(col, row)){
56319             this.view.ensureVisible(row, col, true);
56320           
56321             var r = this.dataSource.getAt(row);
56322             var field = this.colModel.getDataIndex(col);
56323             var cell = Roo.get(this.view.getCell(row,col));
56324             var e = {
56325                 grid: this,
56326                 record: r,
56327                 field: field,
56328                 value: r.data[field],
56329                 row: row,
56330                 column: col,
56331                 cancel:false 
56332             };
56333             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56334                 this.editing = true;
56335                 var ed = this.colModel.getCellEditor(col, row);
56336                 
56337                 if (!ed) {
56338                     return;
56339                 }
56340                 if(!ed.rendered){
56341                     ed.render(ed.parentEl || document.body);
56342                 }
56343                 ed.field.reset();
56344                
56345                 cell.hide();
56346                 
56347                 (function(){ // complex but required for focus issues in safari, ie and opera
56348                     ed.row = row;
56349                     ed.col = col;
56350                     ed.record = r;
56351                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56352                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56353                     this.activeEditor = ed;
56354                     var v = r.data[field];
56355                     ed.startEdit(this.view.getCell(row, col), v);
56356                     // combo's with 'displayField and name set
56357                     if (ed.field.displayField && ed.field.name) {
56358                         ed.field.el.dom.value = r.data[ed.field.name];
56359                     }
56360                     
56361                     
56362                 }).defer(50, this);
56363             }
56364         }
56365     },
56366         
56367     /**
56368      * Stops any active editing
56369      */
56370     stopEditing : function(){
56371         if(this.activeEditor){
56372             this.activeEditor.completeEdit();
56373         }
56374         this.activeEditor = null;
56375     },
56376         
56377          /**
56378      * Called to get grid's drag proxy text, by default returns this.ddText.
56379      * @return {String}
56380      */
56381     getDragDropText : function(){
56382         var count = this.selModel.getSelectedCell() ? 1 : 0;
56383         return String.format(this.ddText, count, count == 1 ? '' : 's');
56384     }
56385         
56386 });/*
56387  * Based on:
56388  * Ext JS Library 1.1.1
56389  * Copyright(c) 2006-2007, Ext JS, LLC.
56390  *
56391  * Originally Released Under LGPL - original licence link has changed is not relivant.
56392  *
56393  * Fork - LGPL
56394  * <script type="text/javascript">
56395  */
56396
56397 // private - not really -- you end up using it !
56398 // This is a support class used internally by the Grid components
56399
56400 /**
56401  * @class Roo.grid.GridEditor
56402  * @extends Roo.Editor
56403  * Class for creating and editable grid elements.
56404  * @param {Object} config any settings (must include field)
56405  */
56406 Roo.grid.GridEditor = function(field, config){
56407     if (!config && field.field) {
56408         config = field;
56409         field = Roo.factory(config.field, Roo.form);
56410     }
56411     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56412     field.monitorTab = false;
56413 };
56414
56415 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56416     
56417     /**
56418      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56419      */
56420     
56421     alignment: "tl-tl",
56422     autoSize: "width",
56423     hideEl : false,
56424     cls: "x-small-editor x-grid-editor",
56425     shim:false,
56426     shadow:"frame"
56427 });/*
56428  * Based on:
56429  * Ext JS Library 1.1.1
56430  * Copyright(c) 2006-2007, Ext JS, LLC.
56431  *
56432  * Originally Released Under LGPL - original licence link has changed is not relivant.
56433  *
56434  * Fork - LGPL
56435  * <script type="text/javascript">
56436  */
56437   
56438
56439   
56440 Roo.grid.PropertyRecord = Roo.data.Record.create([
56441     {name:'name',type:'string'},  'value'
56442 ]);
56443
56444
56445 Roo.grid.PropertyStore = function(grid, source){
56446     this.grid = grid;
56447     this.store = new Roo.data.Store({
56448         recordType : Roo.grid.PropertyRecord
56449     });
56450     this.store.on('update', this.onUpdate,  this);
56451     if(source){
56452         this.setSource(source);
56453     }
56454     Roo.grid.PropertyStore.superclass.constructor.call(this);
56455 };
56456
56457
56458
56459 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56460     setSource : function(o){
56461         this.source = o;
56462         this.store.removeAll();
56463         var data = [];
56464         for(var k in o){
56465             if(this.isEditableValue(o[k])){
56466                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56467             }
56468         }
56469         this.store.loadRecords({records: data}, {}, true);
56470     },
56471
56472     onUpdate : function(ds, record, type){
56473         if(type == Roo.data.Record.EDIT){
56474             var v = record.data['value'];
56475             var oldValue = record.modified['value'];
56476             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56477                 this.source[record.id] = v;
56478                 record.commit();
56479                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56480             }else{
56481                 record.reject();
56482             }
56483         }
56484     },
56485
56486     getProperty : function(row){
56487        return this.store.getAt(row);
56488     },
56489
56490     isEditableValue: function(val){
56491         if(val && val instanceof Date){
56492             return true;
56493         }else if(typeof val == 'object' || typeof val == 'function'){
56494             return false;
56495         }
56496         return true;
56497     },
56498
56499     setValue : function(prop, value){
56500         this.source[prop] = value;
56501         this.store.getById(prop).set('value', value);
56502     },
56503
56504     getSource : function(){
56505         return this.source;
56506     }
56507 });
56508
56509 Roo.grid.PropertyColumnModel = function(grid, store){
56510     this.grid = grid;
56511     var g = Roo.grid;
56512     g.PropertyColumnModel.superclass.constructor.call(this, [
56513         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56514         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56515     ]);
56516     this.store = store;
56517     this.bselect = Roo.DomHelper.append(document.body, {
56518         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56519             {tag: 'option', value: 'true', html: 'true'},
56520             {tag: 'option', value: 'false', html: 'false'}
56521         ]
56522     });
56523     Roo.id(this.bselect);
56524     var f = Roo.form;
56525     this.editors = {
56526         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56527         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56528         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56529         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56530         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56531     };
56532     this.renderCellDelegate = this.renderCell.createDelegate(this);
56533     this.renderPropDelegate = this.renderProp.createDelegate(this);
56534 };
56535
56536 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56537     
56538     
56539     nameText : 'Name',
56540     valueText : 'Value',
56541     
56542     dateFormat : 'm/j/Y',
56543     
56544     
56545     renderDate : function(dateVal){
56546         return dateVal.dateFormat(this.dateFormat);
56547     },
56548
56549     renderBool : function(bVal){
56550         return bVal ? 'true' : 'false';
56551     },
56552
56553     isCellEditable : function(colIndex, rowIndex){
56554         return colIndex == 1;
56555     },
56556
56557     getRenderer : function(col){
56558         return col == 1 ?
56559             this.renderCellDelegate : this.renderPropDelegate;
56560     },
56561
56562     renderProp : function(v){
56563         return this.getPropertyName(v);
56564     },
56565
56566     renderCell : function(val){
56567         var rv = val;
56568         if(val instanceof Date){
56569             rv = this.renderDate(val);
56570         }else if(typeof val == 'boolean'){
56571             rv = this.renderBool(val);
56572         }
56573         return Roo.util.Format.htmlEncode(rv);
56574     },
56575
56576     getPropertyName : function(name){
56577         var pn = this.grid.propertyNames;
56578         return pn && pn[name] ? pn[name] : name;
56579     },
56580
56581     getCellEditor : function(colIndex, rowIndex){
56582         var p = this.store.getProperty(rowIndex);
56583         var n = p.data['name'], val = p.data['value'];
56584         
56585         if(typeof(this.grid.customEditors[n]) == 'string'){
56586             return this.editors[this.grid.customEditors[n]];
56587         }
56588         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56589             return this.grid.customEditors[n];
56590         }
56591         if(val instanceof Date){
56592             return this.editors['date'];
56593         }else if(typeof val == 'number'){
56594             return this.editors['number'];
56595         }else if(typeof val == 'boolean'){
56596             return this.editors['boolean'];
56597         }else{
56598             return this.editors['string'];
56599         }
56600     }
56601 });
56602
56603 /**
56604  * @class Roo.grid.PropertyGrid
56605  * @extends Roo.grid.EditorGrid
56606  * This class represents the  interface of a component based property grid control.
56607  * <br><br>Usage:<pre><code>
56608  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56609       
56610  });
56611  // set any options
56612  grid.render();
56613  * </code></pre>
56614   
56615  * @constructor
56616  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56617  * The container MUST have some type of size defined for the grid to fill. The container will be
56618  * automatically set to position relative if it isn't already.
56619  * @param {Object} config A config object that sets properties on this grid.
56620  */
56621 Roo.grid.PropertyGrid = function(container, config){
56622     config = config || {};
56623     var store = new Roo.grid.PropertyStore(this);
56624     this.store = store;
56625     var cm = new Roo.grid.PropertyColumnModel(this, store);
56626     store.store.sort('name', 'ASC');
56627     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56628         ds: store.store,
56629         cm: cm,
56630         enableColLock:false,
56631         enableColumnMove:false,
56632         stripeRows:false,
56633         trackMouseOver: false,
56634         clicksToEdit:1
56635     }, config));
56636     this.getGridEl().addClass('x-props-grid');
56637     this.lastEditRow = null;
56638     this.on('columnresize', this.onColumnResize, this);
56639     this.addEvents({
56640          /**
56641              * @event beforepropertychange
56642              * Fires before a property changes (return false to stop?)
56643              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56644              * @param {String} id Record Id
56645              * @param {String} newval New Value
56646          * @param {String} oldval Old Value
56647              */
56648         "beforepropertychange": true,
56649         /**
56650              * @event propertychange
56651              * Fires after a property changes
56652              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56653              * @param {String} id Record Id
56654              * @param {String} newval New Value
56655          * @param {String} oldval Old Value
56656              */
56657         "propertychange": true
56658     });
56659     this.customEditors = this.customEditors || {};
56660 };
56661 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56662     
56663      /**
56664      * @cfg {Object} customEditors map of colnames=> custom editors.
56665      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56666      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56667      * false disables editing of the field.
56668          */
56669     
56670       /**
56671      * @cfg {Object} propertyNames map of property Names to their displayed value
56672          */
56673     
56674     render : function(){
56675         Roo.grid.PropertyGrid.superclass.render.call(this);
56676         this.autoSize.defer(100, this);
56677     },
56678
56679     autoSize : function(){
56680         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56681         if(this.view){
56682             this.view.fitColumns();
56683         }
56684     },
56685
56686     onColumnResize : function(){
56687         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56688         this.autoSize();
56689     },
56690     /**
56691      * Sets the data for the Grid
56692      * accepts a Key => Value object of all the elements avaiable.
56693      * @param {Object} data  to appear in grid.
56694      */
56695     setSource : function(source){
56696         this.store.setSource(source);
56697         //this.autoSize();
56698     },
56699     /**
56700      * Gets all the data from the grid.
56701      * @return {Object} data  data stored in grid
56702      */
56703     getSource : function(){
56704         return this.store.getSource();
56705     }
56706 });/*
56707   
56708  * Licence LGPL
56709  
56710  */
56711  
56712 /**
56713  * @class Roo.grid.Calendar
56714  * @extends Roo.util.Grid
56715  * This class extends the Grid to provide a calendar widget
56716  * <br><br>Usage:<pre><code>
56717  var grid = new Roo.grid.Calendar("my-container-id", {
56718      ds: myDataStore,
56719      cm: myColModel,
56720      selModel: mySelectionModel,
56721      autoSizeColumns: true,
56722      monitorWindowResize: false,
56723      trackMouseOver: true
56724      eventstore : real data store..
56725  });
56726  // set any options
56727  grid.render();
56728   
56729   * @constructor
56730  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56731  * The container MUST have some type of size defined for the grid to fill. The container will be
56732  * automatically set to position relative if it isn't already.
56733  * @param {Object} config A config object that sets properties on this grid.
56734  */
56735 Roo.grid.Calendar = function(container, config){
56736         // initialize the container
56737         this.container = Roo.get(container);
56738         this.container.update("");
56739         this.container.setStyle("overflow", "hidden");
56740     this.container.addClass('x-grid-container');
56741
56742     this.id = this.container.id;
56743
56744     Roo.apply(this, config);
56745     // check and correct shorthanded configs
56746     
56747     var rows = [];
56748     var d =1;
56749     for (var r = 0;r < 6;r++) {
56750         
56751         rows[r]=[];
56752         for (var c =0;c < 7;c++) {
56753             rows[r][c]= '';
56754         }
56755     }
56756     if (this.eventStore) {
56757         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56758         this.eventStore.on('load',this.onLoad, this);
56759         this.eventStore.on('beforeload',this.clearEvents, this);
56760          
56761     }
56762     
56763     this.dataSource = new Roo.data.Store({
56764             proxy: new Roo.data.MemoryProxy(rows),
56765             reader: new Roo.data.ArrayReader({}, [
56766                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56767     });
56768
56769     this.dataSource.load();
56770     this.ds = this.dataSource;
56771     this.ds.xmodule = this.xmodule || false;
56772     
56773     
56774     var cellRender = function(v,x,r)
56775     {
56776         return String.format(
56777             '<div class="fc-day  fc-widget-content"><div>' +
56778                 '<div class="fc-event-container"></div>' +
56779                 '<div class="fc-day-number">{0}</div>'+
56780                 
56781                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56782             '</div></div>', v);
56783     
56784     }
56785     
56786     
56787     this.colModel = new Roo.grid.ColumnModel( [
56788         {
56789             xtype: 'ColumnModel',
56790             xns: Roo.grid,
56791             dataIndex : 'weekday0',
56792             header : 'Sunday',
56793             renderer : cellRender
56794         },
56795         {
56796             xtype: 'ColumnModel',
56797             xns: Roo.grid,
56798             dataIndex : 'weekday1',
56799             header : 'Monday',
56800             renderer : cellRender
56801         },
56802         {
56803             xtype: 'ColumnModel',
56804             xns: Roo.grid,
56805             dataIndex : 'weekday2',
56806             header : 'Tuesday',
56807             renderer : cellRender
56808         },
56809         {
56810             xtype: 'ColumnModel',
56811             xns: Roo.grid,
56812             dataIndex : 'weekday3',
56813             header : 'Wednesday',
56814             renderer : cellRender
56815         },
56816         {
56817             xtype: 'ColumnModel',
56818             xns: Roo.grid,
56819             dataIndex : 'weekday4',
56820             header : 'Thursday',
56821             renderer : cellRender
56822         },
56823         {
56824             xtype: 'ColumnModel',
56825             xns: Roo.grid,
56826             dataIndex : 'weekday5',
56827             header : 'Friday',
56828             renderer : cellRender
56829         },
56830         {
56831             xtype: 'ColumnModel',
56832             xns: Roo.grid,
56833             dataIndex : 'weekday6',
56834             header : 'Saturday',
56835             renderer : cellRender
56836         }
56837     ]);
56838     this.cm = this.colModel;
56839     this.cm.xmodule = this.xmodule || false;
56840  
56841         
56842           
56843     //this.selModel = new Roo.grid.CellSelectionModel();
56844     //this.sm = this.selModel;
56845     //this.selModel.init(this);
56846     
56847     
56848     if(this.width){
56849         this.container.setWidth(this.width);
56850     }
56851
56852     if(this.height){
56853         this.container.setHeight(this.height);
56854     }
56855     /** @private */
56856         this.addEvents({
56857         // raw events
56858         /**
56859          * @event click
56860          * The raw click event for the entire grid.
56861          * @param {Roo.EventObject} e
56862          */
56863         "click" : true,
56864         /**
56865          * @event dblclick
56866          * The raw dblclick event for the entire grid.
56867          * @param {Roo.EventObject} e
56868          */
56869         "dblclick" : true,
56870         /**
56871          * @event contextmenu
56872          * The raw contextmenu event for the entire grid.
56873          * @param {Roo.EventObject} e
56874          */
56875         "contextmenu" : true,
56876         /**
56877          * @event mousedown
56878          * The raw mousedown event for the entire grid.
56879          * @param {Roo.EventObject} e
56880          */
56881         "mousedown" : true,
56882         /**
56883          * @event mouseup
56884          * The raw mouseup event for the entire grid.
56885          * @param {Roo.EventObject} e
56886          */
56887         "mouseup" : true,
56888         /**
56889          * @event mouseover
56890          * The raw mouseover event for the entire grid.
56891          * @param {Roo.EventObject} e
56892          */
56893         "mouseover" : true,
56894         /**
56895          * @event mouseout
56896          * The raw mouseout event for the entire grid.
56897          * @param {Roo.EventObject} e
56898          */
56899         "mouseout" : true,
56900         /**
56901          * @event keypress
56902          * The raw keypress event for the entire grid.
56903          * @param {Roo.EventObject} e
56904          */
56905         "keypress" : true,
56906         /**
56907          * @event keydown
56908          * The raw keydown event for the entire grid.
56909          * @param {Roo.EventObject} e
56910          */
56911         "keydown" : true,
56912
56913         // custom events
56914
56915         /**
56916          * @event cellclick
56917          * Fires when a cell is clicked
56918          * @param {Grid} this
56919          * @param {Number} rowIndex
56920          * @param {Number} columnIndex
56921          * @param {Roo.EventObject} e
56922          */
56923         "cellclick" : true,
56924         /**
56925          * @event celldblclick
56926          * Fires when a cell is double clicked
56927          * @param {Grid} this
56928          * @param {Number} rowIndex
56929          * @param {Number} columnIndex
56930          * @param {Roo.EventObject} e
56931          */
56932         "celldblclick" : true,
56933         /**
56934          * @event rowclick
56935          * Fires when a row is clicked
56936          * @param {Grid} this
56937          * @param {Number} rowIndex
56938          * @param {Roo.EventObject} e
56939          */
56940         "rowclick" : true,
56941         /**
56942          * @event rowdblclick
56943          * Fires when a row is double clicked
56944          * @param {Grid} this
56945          * @param {Number} rowIndex
56946          * @param {Roo.EventObject} e
56947          */
56948         "rowdblclick" : true,
56949         /**
56950          * @event headerclick
56951          * Fires when a header is clicked
56952          * @param {Grid} this
56953          * @param {Number} columnIndex
56954          * @param {Roo.EventObject} e
56955          */
56956         "headerclick" : true,
56957         /**
56958          * @event headerdblclick
56959          * Fires when a header cell is double clicked
56960          * @param {Grid} this
56961          * @param {Number} columnIndex
56962          * @param {Roo.EventObject} e
56963          */
56964         "headerdblclick" : true,
56965         /**
56966          * @event rowcontextmenu
56967          * Fires when a row is right clicked
56968          * @param {Grid} this
56969          * @param {Number} rowIndex
56970          * @param {Roo.EventObject} e
56971          */
56972         "rowcontextmenu" : true,
56973         /**
56974          * @event cellcontextmenu
56975          * Fires when a cell is right clicked
56976          * @param {Grid} this
56977          * @param {Number} rowIndex
56978          * @param {Number} cellIndex
56979          * @param {Roo.EventObject} e
56980          */
56981          "cellcontextmenu" : true,
56982         /**
56983          * @event headercontextmenu
56984          * Fires when a header is right clicked
56985          * @param {Grid} this
56986          * @param {Number} columnIndex
56987          * @param {Roo.EventObject} e
56988          */
56989         "headercontextmenu" : true,
56990         /**
56991          * @event bodyscroll
56992          * Fires when the body element is scrolled
56993          * @param {Number} scrollLeft
56994          * @param {Number} scrollTop
56995          */
56996         "bodyscroll" : true,
56997         /**
56998          * @event columnresize
56999          * Fires when the user resizes a column
57000          * @param {Number} columnIndex
57001          * @param {Number} newSize
57002          */
57003         "columnresize" : true,
57004         /**
57005          * @event columnmove
57006          * Fires when the user moves a column
57007          * @param {Number} oldIndex
57008          * @param {Number} newIndex
57009          */
57010         "columnmove" : true,
57011         /**
57012          * @event startdrag
57013          * Fires when row(s) start being dragged
57014          * @param {Grid} this
57015          * @param {Roo.GridDD} dd The drag drop object
57016          * @param {event} e The raw browser event
57017          */
57018         "startdrag" : true,
57019         /**
57020          * @event enddrag
57021          * Fires when a drag operation is complete
57022          * @param {Grid} this
57023          * @param {Roo.GridDD} dd The drag drop object
57024          * @param {event} e The raw browser event
57025          */
57026         "enddrag" : true,
57027         /**
57028          * @event dragdrop
57029          * Fires when dragged row(s) are dropped on a valid DD target
57030          * @param {Grid} this
57031          * @param {Roo.GridDD} dd The drag drop object
57032          * @param {String} targetId The target drag drop object
57033          * @param {event} e The raw browser event
57034          */
57035         "dragdrop" : true,
57036         /**
57037          * @event dragover
57038          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57039          * @param {Grid} this
57040          * @param {Roo.GridDD} dd The drag drop object
57041          * @param {String} targetId The target drag drop object
57042          * @param {event} e The raw browser event
57043          */
57044         "dragover" : true,
57045         /**
57046          * @event dragenter
57047          *  Fires when the dragged row(s) first cross another DD target while being dragged
57048          * @param {Grid} this
57049          * @param {Roo.GridDD} dd The drag drop object
57050          * @param {String} targetId The target drag drop object
57051          * @param {event} e The raw browser event
57052          */
57053         "dragenter" : true,
57054         /**
57055          * @event dragout
57056          * Fires when the dragged row(s) leave another DD target while being dragged
57057          * @param {Grid} this
57058          * @param {Roo.GridDD} dd The drag drop object
57059          * @param {String} targetId The target drag drop object
57060          * @param {event} e The raw browser event
57061          */
57062         "dragout" : true,
57063         /**
57064          * @event rowclass
57065          * Fires when a row is rendered, so you can change add a style to it.
57066          * @param {GridView} gridview   The grid view
57067          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57068          */
57069         'rowclass' : true,
57070
57071         /**
57072          * @event render
57073          * Fires when the grid is rendered
57074          * @param {Grid} grid
57075          */
57076         'render' : true,
57077             /**
57078              * @event select
57079              * Fires when a date is selected
57080              * @param {DatePicker} this
57081              * @param {Date} date The selected date
57082              */
57083         'select': true,
57084         /**
57085              * @event monthchange
57086              * Fires when the displayed month changes 
57087              * @param {DatePicker} this
57088              * @param {Date} date The selected month
57089              */
57090         'monthchange': true,
57091         /**
57092              * @event evententer
57093              * Fires when mouse over an event
57094              * @param {Calendar} this
57095              * @param {event} Event
57096              */
57097         'evententer': true,
57098         /**
57099              * @event eventleave
57100              * Fires when the mouse leaves an
57101              * @param {Calendar} this
57102              * @param {event}
57103              */
57104         'eventleave': true,
57105         /**
57106              * @event eventclick
57107              * Fires when the mouse click an
57108              * @param {Calendar} this
57109              * @param {event}
57110              */
57111         'eventclick': true,
57112         /**
57113              * @event eventrender
57114              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57115              * @param {Calendar} this
57116              * @param {data} data to be modified
57117              */
57118         'eventrender': true
57119         
57120     });
57121
57122     Roo.grid.Grid.superclass.constructor.call(this);
57123     this.on('render', function() {
57124         this.view.el.addClass('x-grid-cal'); 
57125         
57126         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57127
57128     },this);
57129     
57130     if (!Roo.grid.Calendar.style) {
57131         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57132             
57133             
57134             '.x-grid-cal .x-grid-col' :  {
57135                 height: 'auto !important',
57136                 'vertical-align': 'top'
57137             },
57138             '.x-grid-cal  .fc-event-hori' : {
57139                 height: '14px'
57140             }
57141              
57142             
57143         }, Roo.id());
57144     }
57145
57146     
57147     
57148 };
57149 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57150     /**
57151      * @cfg {Store} eventStore The store that loads events.
57152      */
57153     eventStore : 25,
57154
57155      
57156     activeDate : false,
57157     startDay : 0,
57158     autoWidth : true,
57159     monitorWindowResize : false,
57160
57161     
57162     resizeColumns : function() {
57163         var col = (this.view.el.getWidth() / 7) - 3;
57164         // loop through cols, and setWidth
57165         for(var i =0 ; i < 7 ; i++){
57166             this.cm.setColumnWidth(i, col);
57167         }
57168     },
57169      setDate :function(date) {
57170         
57171         Roo.log('setDate?');
57172         
57173         this.resizeColumns();
57174         var vd = this.activeDate;
57175         this.activeDate = date;
57176 //        if(vd && this.el){
57177 //            var t = date.getTime();
57178 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57179 //                Roo.log('using add remove');
57180 //                
57181 //                this.fireEvent('monthchange', this, date);
57182 //                
57183 //                this.cells.removeClass("fc-state-highlight");
57184 //                this.cells.each(function(c){
57185 //                   if(c.dateValue == t){
57186 //                       c.addClass("fc-state-highlight");
57187 //                       setTimeout(function(){
57188 //                            try{c.dom.firstChild.focus();}catch(e){}
57189 //                       }, 50);
57190 //                       return false;
57191 //                   }
57192 //                   return true;
57193 //                });
57194 //                return;
57195 //            }
57196 //        }
57197         
57198         var days = date.getDaysInMonth();
57199         
57200         var firstOfMonth = date.getFirstDateOfMonth();
57201         var startingPos = firstOfMonth.getDay()-this.startDay;
57202         
57203         if(startingPos < this.startDay){
57204             startingPos += 7;
57205         }
57206         
57207         var pm = date.add(Date.MONTH, -1);
57208         var prevStart = pm.getDaysInMonth()-startingPos;
57209 //        
57210         
57211         
57212         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57213         
57214         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57215         //this.cells.addClassOnOver('fc-state-hover');
57216         
57217         var cells = this.cells.elements;
57218         var textEls = this.textNodes;
57219         
57220         //Roo.each(cells, function(cell){
57221         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57222         //});
57223         
57224         days += startingPos;
57225
57226         // convert everything to numbers so it's fast
57227         var day = 86400000;
57228         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57229         //Roo.log(d);
57230         //Roo.log(pm);
57231         //Roo.log(prevStart);
57232         
57233         var today = new Date().clearTime().getTime();
57234         var sel = date.clearTime().getTime();
57235         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57236         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57237         var ddMatch = this.disabledDatesRE;
57238         var ddText = this.disabledDatesText;
57239         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57240         var ddaysText = this.disabledDaysText;
57241         var format = this.format;
57242         
57243         var setCellClass = function(cal, cell){
57244             
57245             //Roo.log('set Cell Class');
57246             cell.title = "";
57247             var t = d.getTime();
57248             
57249             //Roo.log(d);
57250             
57251             
57252             cell.dateValue = t;
57253             if(t == today){
57254                 cell.className += " fc-today";
57255                 cell.className += " fc-state-highlight";
57256                 cell.title = cal.todayText;
57257             }
57258             if(t == sel){
57259                 // disable highlight in other month..
57260                 cell.className += " fc-state-highlight";
57261                 
57262             }
57263             // disabling
57264             if(t < min) {
57265                 //cell.className = " fc-state-disabled";
57266                 cell.title = cal.minText;
57267                 return;
57268             }
57269             if(t > max) {
57270                 //cell.className = " fc-state-disabled";
57271                 cell.title = cal.maxText;
57272                 return;
57273             }
57274             if(ddays){
57275                 if(ddays.indexOf(d.getDay()) != -1){
57276                     // cell.title = ddaysText;
57277                    // cell.className = " fc-state-disabled";
57278                 }
57279             }
57280             if(ddMatch && format){
57281                 var fvalue = d.dateFormat(format);
57282                 if(ddMatch.test(fvalue)){
57283                     cell.title = ddText.replace("%0", fvalue);
57284                    cell.className = " fc-state-disabled";
57285                 }
57286             }
57287             
57288             if (!cell.initialClassName) {
57289                 cell.initialClassName = cell.dom.className;
57290             }
57291             
57292             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57293         };
57294
57295         var i = 0;
57296         
57297         for(; i < startingPos; i++) {
57298             cells[i].dayName =  (++prevStart);
57299             Roo.log(textEls[i]);
57300             d.setDate(d.getDate()+1);
57301             
57302             //cells[i].className = "fc-past fc-other-month";
57303             setCellClass(this, cells[i]);
57304         }
57305         
57306         var intDay = 0;
57307         
57308         for(; i < days; i++){
57309             intDay = i - startingPos + 1;
57310             cells[i].dayName =  (intDay);
57311             d.setDate(d.getDate()+1);
57312             
57313             cells[i].className = ''; // "x-date-active";
57314             setCellClass(this, cells[i]);
57315         }
57316         var extraDays = 0;
57317         
57318         for(; i < 42; i++) {
57319             //textEls[i].innerHTML = (++extraDays);
57320             
57321             d.setDate(d.getDate()+1);
57322             cells[i].dayName = (++extraDays);
57323             cells[i].className = "fc-future fc-other-month";
57324             setCellClass(this, cells[i]);
57325         }
57326         
57327         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57328         
57329         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57330         
57331         // this will cause all the cells to mis
57332         var rows= [];
57333         var i =0;
57334         for (var r = 0;r < 6;r++) {
57335             for (var c =0;c < 7;c++) {
57336                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57337             }    
57338         }
57339         
57340         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57341         for(i=0;i<cells.length;i++) {
57342             
57343             this.cells.elements[i].dayName = cells[i].dayName ;
57344             this.cells.elements[i].className = cells[i].className;
57345             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57346             this.cells.elements[i].title = cells[i].title ;
57347             this.cells.elements[i].dateValue = cells[i].dateValue ;
57348         }
57349         
57350         
57351         
57352         
57353         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57354         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57355         
57356         ////if(totalRows != 6){
57357             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57358            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57359        // }
57360         
57361         this.fireEvent('monthchange', this, date);
57362         
57363         
57364     },
57365  /**
57366      * Returns the grid's SelectionModel.
57367      * @return {SelectionModel}
57368      */
57369     getSelectionModel : function(){
57370         if(!this.selModel){
57371             this.selModel = new Roo.grid.CellSelectionModel();
57372         }
57373         return this.selModel;
57374     },
57375
57376     load: function() {
57377         this.eventStore.load()
57378         
57379         
57380         
57381     },
57382     
57383     findCell : function(dt) {
57384         dt = dt.clearTime().getTime();
57385         var ret = false;
57386         this.cells.each(function(c){
57387             //Roo.log("check " +c.dateValue + '?=' + dt);
57388             if(c.dateValue == dt){
57389                 ret = c;
57390                 return false;
57391             }
57392             return true;
57393         });
57394         
57395         return ret;
57396     },
57397     
57398     findCells : function(rec) {
57399         var s = rec.data.start_dt.clone().clearTime().getTime();
57400        // Roo.log(s);
57401         var e= rec.data.end_dt.clone().clearTime().getTime();
57402        // Roo.log(e);
57403         var ret = [];
57404         this.cells.each(function(c){
57405              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57406             
57407             if(c.dateValue > e){
57408                 return ;
57409             }
57410             if(c.dateValue < s){
57411                 return ;
57412             }
57413             ret.push(c);
57414         });
57415         
57416         return ret;    
57417     },
57418     
57419     findBestRow: function(cells)
57420     {
57421         var ret = 0;
57422         
57423         for (var i =0 ; i < cells.length;i++) {
57424             ret  = Math.max(cells[i].rows || 0,ret);
57425         }
57426         return ret;
57427         
57428     },
57429     
57430     
57431     addItem : function(rec)
57432     {
57433         // look for vertical location slot in
57434         var cells = this.findCells(rec);
57435         
57436         rec.row = this.findBestRow(cells);
57437         
57438         // work out the location.
57439         
57440         var crow = false;
57441         var rows = [];
57442         for(var i =0; i < cells.length; i++) {
57443             if (!crow) {
57444                 crow = {
57445                     start : cells[i],
57446                     end :  cells[i]
57447                 };
57448                 continue;
57449             }
57450             if (crow.start.getY() == cells[i].getY()) {
57451                 // on same row.
57452                 crow.end = cells[i];
57453                 continue;
57454             }
57455             // different row.
57456             rows.push(crow);
57457             crow = {
57458                 start: cells[i],
57459                 end : cells[i]
57460             };
57461             
57462         }
57463         
57464         rows.push(crow);
57465         rec.els = [];
57466         rec.rows = rows;
57467         rec.cells = cells;
57468         for (var i = 0; i < cells.length;i++) {
57469             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57470             
57471         }
57472         
57473         
57474     },
57475     
57476     clearEvents: function() {
57477         
57478         if (!this.eventStore.getCount()) {
57479             return;
57480         }
57481         // reset number of rows in cells.
57482         Roo.each(this.cells.elements, function(c){
57483             c.rows = 0;
57484         });
57485         
57486         this.eventStore.each(function(e) {
57487             this.clearEvent(e);
57488         },this);
57489         
57490     },
57491     
57492     clearEvent : function(ev)
57493     {
57494         if (ev.els) {
57495             Roo.each(ev.els, function(el) {
57496                 el.un('mouseenter' ,this.onEventEnter, this);
57497                 el.un('mouseleave' ,this.onEventLeave, this);
57498                 el.remove();
57499             },this);
57500             ev.els = [];
57501         }
57502     },
57503     
57504     
57505     renderEvent : function(ev,ctr) {
57506         if (!ctr) {
57507              ctr = this.view.el.select('.fc-event-container',true).first();
57508         }
57509         
57510          
57511         this.clearEvent(ev);
57512             //code
57513        
57514         
57515         
57516         ev.els = [];
57517         var cells = ev.cells;
57518         var rows = ev.rows;
57519         this.fireEvent('eventrender', this, ev);
57520         
57521         for(var i =0; i < rows.length; i++) {
57522             
57523             cls = '';
57524             if (i == 0) {
57525                 cls += ' fc-event-start';
57526             }
57527             if ((i+1) == rows.length) {
57528                 cls += ' fc-event-end';
57529             }
57530             
57531             //Roo.log(ev.data);
57532             // how many rows should it span..
57533             var cg = this.eventTmpl.append(ctr,Roo.apply({
57534                 fccls : cls
57535                 
57536             }, ev.data) , true);
57537             
57538             
57539             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57540             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57541             cg.on('click', this.onEventClick, this, ev);
57542             
57543             ev.els.push(cg);
57544             
57545             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57546             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57547             //Roo.log(cg);
57548              
57549             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57550             cg.setWidth(ebox.right - sbox.x -2);
57551         }
57552     },
57553     
57554     renderEvents: function()
57555     {   
57556         // first make sure there is enough space..
57557         
57558         if (!this.eventTmpl) {
57559             this.eventTmpl = new Roo.Template(
57560                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57561                     '<div class="fc-event-inner">' +
57562                         '<span class="fc-event-time">{time}</span>' +
57563                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57564                     '</div>' +
57565                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57566                 '</div>'
57567             );
57568                 
57569         }
57570                
57571         
57572         
57573         this.cells.each(function(c) {
57574             //Roo.log(c.select('.fc-day-content div',true).first());
57575             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57576         });
57577         
57578         var ctr = this.view.el.select('.fc-event-container',true).first();
57579         
57580         var cls;
57581         this.eventStore.each(function(ev){
57582             
57583             this.renderEvent(ev);
57584              
57585              
57586         }, this);
57587         this.view.layout();
57588         
57589     },
57590     
57591     onEventEnter: function (e, el,event,d) {
57592         this.fireEvent('evententer', this, el, event);
57593     },
57594     
57595     onEventLeave: function (e, el,event,d) {
57596         this.fireEvent('eventleave', this, el, event);
57597     },
57598     
57599     onEventClick: function (e, el,event,d) {
57600         this.fireEvent('eventclick', this, el, event);
57601     },
57602     
57603     onMonthChange: function () {
57604         this.store.load();
57605     },
57606     
57607     onLoad: function () {
57608         
57609         //Roo.log('calendar onload');
57610 //         
57611         if(this.eventStore.getCount() > 0){
57612             
57613            
57614             
57615             this.eventStore.each(function(d){
57616                 
57617                 
57618                 // FIXME..
57619                 var add =   d.data;
57620                 if (typeof(add.end_dt) == 'undefined')  {
57621                     Roo.log("Missing End time in calendar data: ");
57622                     Roo.log(d);
57623                     return;
57624                 }
57625                 if (typeof(add.start_dt) == 'undefined')  {
57626                     Roo.log("Missing Start time in calendar data: ");
57627                     Roo.log(d);
57628                     return;
57629                 }
57630                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57631                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57632                 add.id = add.id || d.id;
57633                 add.title = add.title || '??';
57634                 
57635                 this.addItem(d);
57636                 
57637              
57638             },this);
57639         }
57640         
57641         this.renderEvents();
57642     }
57643     
57644
57645 });
57646 /*
57647  grid : {
57648                 xtype: 'Grid',
57649                 xns: Roo.grid,
57650                 listeners : {
57651                     render : function ()
57652                     {
57653                         _this.grid = this;
57654                         
57655                         if (!this.view.el.hasClass('course-timesheet')) {
57656                             this.view.el.addClass('course-timesheet');
57657                         }
57658                         if (this.tsStyle) {
57659                             this.ds.load({});
57660                             return; 
57661                         }
57662                         Roo.log('width');
57663                         Roo.log(_this.grid.view.el.getWidth());
57664                         
57665                         
57666                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57667                             '.course-timesheet .x-grid-row' : {
57668                                 height: '80px'
57669                             },
57670                             '.x-grid-row td' : {
57671                                 'vertical-align' : 0
57672                             },
57673                             '.course-edit-link' : {
57674                                 'color' : 'blue',
57675                                 'text-overflow' : 'ellipsis',
57676                                 'overflow' : 'hidden',
57677                                 'white-space' : 'nowrap',
57678                                 'cursor' : 'pointer'
57679                             },
57680                             '.sub-link' : {
57681                                 'color' : 'green'
57682                             },
57683                             '.de-act-sup-link' : {
57684                                 'color' : 'purple',
57685                                 'text-decoration' : 'line-through'
57686                             },
57687                             '.de-act-link' : {
57688                                 'color' : 'red',
57689                                 'text-decoration' : 'line-through'
57690                             },
57691                             '.course-timesheet .course-highlight' : {
57692                                 'border-top-style': 'dashed !important',
57693                                 'border-bottom-bottom': 'dashed !important'
57694                             },
57695                             '.course-timesheet .course-item' : {
57696                                 'font-family'   : 'tahoma, arial, helvetica',
57697                                 'font-size'     : '11px',
57698                                 'overflow'      : 'hidden',
57699                                 'padding-left'  : '10px',
57700                                 'padding-right' : '10px',
57701                                 'padding-top' : '10px' 
57702                             }
57703                             
57704                         }, Roo.id());
57705                                 this.ds.load({});
57706                     }
57707                 },
57708                 autoWidth : true,
57709                 monitorWindowResize : false,
57710                 cellrenderer : function(v,x,r)
57711                 {
57712                     return v;
57713                 },
57714                 sm : {
57715                     xtype: 'CellSelectionModel',
57716                     xns: Roo.grid
57717                 },
57718                 dataSource : {
57719                     xtype: 'Store',
57720                     xns: Roo.data,
57721                     listeners : {
57722                         beforeload : function (_self, options)
57723                         {
57724                             options.params = options.params || {};
57725                             options.params._month = _this.monthField.getValue();
57726                             options.params.limit = 9999;
57727                             options.params['sort'] = 'when_dt';    
57728                             options.params['dir'] = 'ASC';    
57729                             this.proxy.loadResponse = this.loadResponse;
57730                             Roo.log("load?");
57731                             //this.addColumns();
57732                         },
57733                         load : function (_self, records, options)
57734                         {
57735                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57736                                 // if you click on the translation.. you can edit it...
57737                                 var el = Roo.get(this);
57738                                 var id = el.dom.getAttribute('data-id');
57739                                 var d = el.dom.getAttribute('data-date');
57740                                 var t = el.dom.getAttribute('data-time');
57741                                 //var id = this.child('span').dom.textContent;
57742                                 
57743                                 //Roo.log(this);
57744                                 Pman.Dialog.CourseCalendar.show({
57745                                     id : id,
57746                                     when_d : d,
57747                                     when_t : t,
57748                                     productitem_active : id ? 1 : 0
57749                                 }, function() {
57750                                     _this.grid.ds.load({});
57751                                 });
57752                            
57753                            });
57754                            
57755                            _this.panel.fireEvent('resize', [ '', '' ]);
57756                         }
57757                     },
57758                     loadResponse : function(o, success, response){
57759                             // this is overridden on before load..
57760                             
57761                             Roo.log("our code?");       
57762                             //Roo.log(success);
57763                             //Roo.log(response)
57764                             delete this.activeRequest;
57765                             if(!success){
57766                                 this.fireEvent("loadexception", this, o, response);
57767                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57768                                 return;
57769                             }
57770                             var result;
57771                             try {
57772                                 result = o.reader.read(response);
57773                             }catch(e){
57774                                 Roo.log("load exception?");
57775                                 this.fireEvent("loadexception", this, o, response, e);
57776                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57777                                 return;
57778                             }
57779                             Roo.log("ready...");        
57780                             // loop through result.records;
57781                             // and set this.tdate[date] = [] << array of records..
57782                             _this.tdata  = {};
57783                             Roo.each(result.records, function(r){
57784                                 //Roo.log(r.data);
57785                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57786                                     _this.tdata[r.data.when_dt.format('j')] = [];
57787                                 }
57788                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57789                             });
57790                             
57791                             //Roo.log(_this.tdata);
57792                             
57793                             result.records = [];
57794                             result.totalRecords = 6;
57795                     
57796                             // let's generate some duumy records for the rows.
57797                             //var st = _this.dateField.getValue();
57798                             
57799                             // work out monday..
57800                             //st = st.add(Date.DAY, -1 * st.format('w'));
57801                             
57802                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57803                             
57804                             var firstOfMonth = date.getFirstDayOfMonth();
57805                             var days = date.getDaysInMonth();
57806                             var d = 1;
57807                             var firstAdded = false;
57808                             for (var i = 0; i < result.totalRecords ; i++) {
57809                                 //var d= st.add(Date.DAY, i);
57810                                 var row = {};
57811                                 var added = 0;
57812                                 for(var w = 0 ; w < 7 ; w++){
57813                                     if(!firstAdded && firstOfMonth != w){
57814                                         continue;
57815                                     }
57816                                     if(d > days){
57817                                         continue;
57818                                     }
57819                                     firstAdded = true;
57820                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57821                                     row['weekday'+w] = String.format(
57822                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57823                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57824                                                     d,
57825                                                     date.format('Y-m-')+dd
57826                                                 );
57827                                     added++;
57828                                     if(typeof(_this.tdata[d]) != 'undefined'){
57829                                         Roo.each(_this.tdata[d], function(r){
57830                                             var is_sub = '';
57831                                             var deactive = '';
57832                                             var id = r.id;
57833                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57834                                             if(r.parent_id*1>0){
57835                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57836                                                 id = r.parent_id;
57837                                             }
57838                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57839                                                 deactive = 'de-act-link';
57840                                             }
57841                                             
57842                                             row['weekday'+w] += String.format(
57843                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57844                                                     id, //0
57845                                                     r.product_id_name, //1
57846                                                     r.when_dt.format('h:ia'), //2
57847                                                     is_sub, //3
57848                                                     deactive, //4
57849                                                     desc // 5
57850                                             );
57851                                         });
57852                                     }
57853                                     d++;
57854                                 }
57855                                 
57856                                 // only do this if something added..
57857                                 if(added > 0){ 
57858                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57859                                 }
57860                                 
57861                                 
57862                                 // push it twice. (second one with an hour..
57863                                 
57864                             }
57865                             //Roo.log(result);
57866                             this.fireEvent("load", this, o, o.request.arg);
57867                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57868                         },
57869                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57870                     proxy : {
57871                         xtype: 'HttpProxy',
57872                         xns: Roo.data,
57873                         method : 'GET',
57874                         url : baseURL + '/Roo/Shop_course.php'
57875                     },
57876                     reader : {
57877                         xtype: 'JsonReader',
57878                         xns: Roo.data,
57879                         id : 'id',
57880                         fields : [
57881                             {
57882                                 'name': 'id',
57883                                 'type': 'int'
57884                             },
57885                             {
57886                                 'name': 'when_dt',
57887                                 'type': 'string'
57888                             },
57889                             {
57890                                 'name': 'end_dt',
57891                                 'type': 'string'
57892                             },
57893                             {
57894                                 'name': 'parent_id',
57895                                 'type': 'int'
57896                             },
57897                             {
57898                                 'name': 'product_id',
57899                                 'type': 'int'
57900                             },
57901                             {
57902                                 'name': 'productitem_id',
57903                                 'type': 'int'
57904                             },
57905                             {
57906                                 'name': 'guid',
57907                                 'type': 'int'
57908                             }
57909                         ]
57910                     }
57911                 },
57912                 toolbar : {
57913                     xtype: 'Toolbar',
57914                     xns: Roo,
57915                     items : [
57916                         {
57917                             xtype: 'Button',
57918                             xns: Roo.Toolbar,
57919                             listeners : {
57920                                 click : function (_self, e)
57921                                 {
57922                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57923                                     sd.setMonth(sd.getMonth()-1);
57924                                     _this.monthField.setValue(sd.format('Y-m-d'));
57925                                     _this.grid.ds.load({});
57926                                 }
57927                             },
57928                             text : "Back"
57929                         },
57930                         {
57931                             xtype: 'Separator',
57932                             xns: Roo.Toolbar
57933                         },
57934                         {
57935                             xtype: 'MonthField',
57936                             xns: Roo.form,
57937                             listeners : {
57938                                 render : function (_self)
57939                                 {
57940                                     _this.monthField = _self;
57941                                    // _this.monthField.set  today
57942                                 },
57943                                 select : function (combo, date)
57944                                 {
57945                                     _this.grid.ds.load({});
57946                                 }
57947                             },
57948                             value : (function() { return new Date(); })()
57949                         },
57950                         {
57951                             xtype: 'Separator',
57952                             xns: Roo.Toolbar
57953                         },
57954                         {
57955                             xtype: 'TextItem',
57956                             xns: Roo.Toolbar,
57957                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57958                         },
57959                         {
57960                             xtype: 'Fill',
57961                             xns: Roo.Toolbar
57962                         },
57963                         {
57964                             xtype: 'Button',
57965                             xns: Roo.Toolbar,
57966                             listeners : {
57967                                 click : function (_self, e)
57968                                 {
57969                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57970                                     sd.setMonth(sd.getMonth()+1);
57971                                     _this.monthField.setValue(sd.format('Y-m-d'));
57972                                     _this.grid.ds.load({});
57973                                 }
57974                             },
57975                             text : "Next"
57976                         }
57977                     ]
57978                 },
57979                  
57980             }
57981         };
57982         
57983         *//*
57984  * Based on:
57985  * Ext JS Library 1.1.1
57986  * Copyright(c) 2006-2007, Ext JS, LLC.
57987  *
57988  * Originally Released Under LGPL - original licence link has changed is not relivant.
57989  *
57990  * Fork - LGPL
57991  * <script type="text/javascript">
57992  */
57993  
57994 /**
57995  * @class Roo.LoadMask
57996  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57997  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57998  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57999  * element's UpdateManager load indicator and will be destroyed after the initial load.
58000  * @constructor
58001  * Create a new LoadMask
58002  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58003  * @param {Object} config The config object
58004  */
58005 Roo.LoadMask = function(el, config){
58006     this.el = Roo.get(el);
58007     Roo.apply(this, config);
58008     if(this.store){
58009         this.store.on('beforeload', this.onBeforeLoad, this);
58010         this.store.on('load', this.onLoad, this);
58011         this.store.on('loadexception', this.onLoadException, this);
58012         this.removeMask = false;
58013     }else{
58014         var um = this.el.getUpdateManager();
58015         um.showLoadIndicator = false; // disable the default indicator
58016         um.on('beforeupdate', this.onBeforeLoad, this);
58017         um.on('update', this.onLoad, this);
58018         um.on('failure', this.onLoad, this);
58019         this.removeMask = true;
58020     }
58021 };
58022
58023 Roo.LoadMask.prototype = {
58024     /**
58025      * @cfg {Boolean} removeMask
58026      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58027      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58028      */
58029     /**
58030      * @cfg {String} msg
58031      * The text to display in a centered loading message box (defaults to 'Loading...')
58032      */
58033     msg : 'Loading...',
58034     /**
58035      * @cfg {String} msgCls
58036      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58037      */
58038     msgCls : 'x-mask-loading',
58039
58040     /**
58041      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58042      * @type Boolean
58043      */
58044     disabled: false,
58045
58046     /**
58047      * Disables the mask to prevent it from being displayed
58048      */
58049     disable : function(){
58050        this.disabled = true;
58051     },
58052
58053     /**
58054      * Enables the mask so that it can be displayed
58055      */
58056     enable : function(){
58057         this.disabled = false;
58058     },
58059     
58060     onLoadException : function()
58061     {
58062         Roo.log(arguments);
58063         
58064         if (typeof(arguments[3]) != 'undefined') {
58065             Roo.MessageBox.alert("Error loading",arguments[3]);
58066         } 
58067         /*
58068         try {
58069             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58070                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58071             }   
58072         } catch(e) {
58073             
58074         }
58075         */
58076     
58077         
58078         
58079         this.el.unmask(this.removeMask);
58080     },
58081     // private
58082     onLoad : function()
58083     {
58084         this.el.unmask(this.removeMask);
58085     },
58086
58087     // private
58088     onBeforeLoad : function(){
58089         if(!this.disabled){
58090             this.el.mask(this.msg, this.msgCls);
58091         }
58092     },
58093
58094     // private
58095     destroy : function(){
58096         if(this.store){
58097             this.store.un('beforeload', this.onBeforeLoad, this);
58098             this.store.un('load', this.onLoad, this);
58099             this.store.un('loadexception', this.onLoadException, this);
58100         }else{
58101             var um = this.el.getUpdateManager();
58102             um.un('beforeupdate', this.onBeforeLoad, this);
58103             um.un('update', this.onLoad, this);
58104             um.un('failure', this.onLoad, this);
58105         }
58106     }
58107 };/*
58108  * Based on:
58109  * Ext JS Library 1.1.1
58110  * Copyright(c) 2006-2007, Ext JS, LLC.
58111  *
58112  * Originally Released Under LGPL - original licence link has changed is not relivant.
58113  *
58114  * Fork - LGPL
58115  * <script type="text/javascript">
58116  */
58117
58118
58119 /**
58120  * @class Roo.XTemplate
58121  * @extends Roo.Template
58122  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58123 <pre><code>
58124 var t = new Roo.XTemplate(
58125         '&lt;select name="{name}"&gt;',
58126                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58127         '&lt;/select&gt;'
58128 );
58129  
58130 // then append, applying the master template values
58131  </code></pre>
58132  *
58133  * Supported features:
58134  *
58135  *  Tags:
58136
58137 <pre><code>
58138       {a_variable} - output encoded.
58139       {a_variable.format:("Y-m-d")} - call a method on the variable
58140       {a_variable:raw} - unencoded output
58141       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58142       {a_variable:this.method_on_template(...)} - call a method on the template object.
58143  
58144 </code></pre>
58145  *  The tpl tag:
58146 <pre><code>
58147         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58148         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58149         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58150         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58151   
58152         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58153         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58154 </code></pre>
58155  *      
58156  */
58157 Roo.XTemplate = function()
58158 {
58159     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58160     if (this.html) {
58161         this.compile();
58162     }
58163 };
58164
58165
58166 Roo.extend(Roo.XTemplate, Roo.Template, {
58167
58168     /**
58169      * The various sub templates
58170      */
58171     tpls : false,
58172     /**
58173      *
58174      * basic tag replacing syntax
58175      * WORD:WORD()
58176      *
58177      * // you can fake an object call by doing this
58178      *  x.t:(test,tesT) 
58179      * 
58180      */
58181     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58182
58183     /**
58184      * compile the template
58185      *
58186      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58187      *
58188      */
58189     compile: function()
58190     {
58191         var s = this.html;
58192      
58193         s = ['<tpl>', s, '</tpl>'].join('');
58194     
58195         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58196             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58197             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58198             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58199             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58200             m,
58201             id     = 0,
58202             tpls   = [];
58203     
58204         while(true == !!(m = s.match(re))){
58205             var forMatch   = m[0].match(nameRe),
58206                 ifMatch   = m[0].match(ifRe),
58207                 execMatch   = m[0].match(execRe),
58208                 namedMatch   = m[0].match(namedRe),
58209                 
58210                 exp  = null, 
58211                 fn   = null,
58212                 exec = null,
58213                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58214                 
58215             if (ifMatch) {
58216                 // if - puts fn into test..
58217                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58218                 if(exp){
58219                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58220                 }
58221             }
58222             
58223             if (execMatch) {
58224                 // exec - calls a function... returns empty if true is  returned.
58225                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58226                 if(exp){
58227                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58228                 }
58229             }
58230             
58231             
58232             if (name) {
58233                 // for = 
58234                 switch(name){
58235                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58236                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58237                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58238                 }
58239             }
58240             var uid = namedMatch ? namedMatch[1] : id;
58241             
58242             
58243             tpls.push({
58244                 id:     namedMatch ? namedMatch[1] : id,
58245                 target: name,
58246                 exec:   exec,
58247                 test:   fn,
58248                 body:   m[1] || ''
58249             });
58250             if (namedMatch) {
58251                 s = s.replace(m[0], '');
58252             } else { 
58253                 s = s.replace(m[0], '{xtpl'+ id + '}');
58254             }
58255             ++id;
58256         }
58257         this.tpls = [];
58258         for(var i = tpls.length-1; i >= 0; --i){
58259             this.compileTpl(tpls[i]);
58260             this.tpls[tpls[i].id] = tpls[i];
58261         }
58262         this.master = tpls[tpls.length-1];
58263         return this;
58264     },
58265     /**
58266      * same as applyTemplate, except it's done to one of the subTemplates
58267      * when using named templates, you can do:
58268      *
58269      * var str = pl.applySubTemplate('your-name', values);
58270      *
58271      * 
58272      * @param {Number} id of the template
58273      * @param {Object} values to apply to template
58274      * @param {Object} parent (normaly the instance of this object)
58275      */
58276     applySubTemplate : function(id, values, parent)
58277     {
58278         
58279         
58280         var t = this.tpls[id];
58281         
58282         
58283         try { 
58284             if(t.test && !t.test.call(this, values, parent)){
58285                 return '';
58286             }
58287         } catch(e) {
58288             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58289             Roo.log(e.toString());
58290             Roo.log(t.test);
58291             return ''
58292         }
58293         try { 
58294             
58295             if(t.exec && t.exec.call(this, values, parent)){
58296                 return '';
58297             }
58298         } catch(e) {
58299             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58300             Roo.log(e.toString());
58301             Roo.log(t.exec);
58302             return ''
58303         }
58304         try {
58305             var vs = t.target ? t.target.call(this, values, parent) : values;
58306             parent = t.target ? values : parent;
58307             if(t.target && vs instanceof Array){
58308                 var buf = [];
58309                 for(var i = 0, len = vs.length; i < len; i++){
58310                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58311                 }
58312                 return buf.join('');
58313             }
58314             return t.compiled.call(this, vs, parent);
58315         } catch (e) {
58316             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58317             Roo.log(e.toString());
58318             Roo.log(t.compiled);
58319             return '';
58320         }
58321     },
58322
58323     compileTpl : function(tpl)
58324     {
58325         var fm = Roo.util.Format;
58326         var useF = this.disableFormats !== true;
58327         var sep = Roo.isGecko ? "+" : ",";
58328         var undef = function(str) {
58329             Roo.log("Property not found :"  + str);
58330             return '';
58331         };
58332         
58333         var fn = function(m, name, format, args)
58334         {
58335             //Roo.log(arguments);
58336             args = args ? args.replace(/\\'/g,"'") : args;
58337             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58338             if (typeof(format) == 'undefined') {
58339                 format= 'htmlEncode';
58340             }
58341             if (format == 'raw' ) {
58342                 format = false;
58343             }
58344             
58345             if(name.substr(0, 4) == 'xtpl'){
58346                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58347             }
58348             
58349             // build an array of options to determine if value is undefined..
58350             
58351             // basically get 'xxxx.yyyy' then do
58352             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58353             //    (function () { Roo.log("Property not found"); return ''; })() :
58354             //    ......
58355             
58356             var udef_ar = [];
58357             var lookfor = '';
58358             Roo.each(name.split('.'), function(st) {
58359                 lookfor += (lookfor.length ? '.': '') + st;
58360                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58361             });
58362             
58363             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58364             
58365             
58366             if(format && useF){
58367                 
58368                 args = args ? ',' + args : "";
58369                  
58370                 if(format.substr(0, 5) != "this."){
58371                     format = "fm." + format + '(';
58372                 }else{
58373                     format = 'this.call("'+ format.substr(5) + '", ';
58374                     args = ", values";
58375                 }
58376                 
58377                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58378             }
58379              
58380             if (args.length) {
58381                 // called with xxyx.yuu:(test,test)
58382                 // change to ()
58383                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58384             }
58385             // raw.. - :raw modifier..
58386             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58387             
58388         };
58389         var body;
58390         // branched to use + in gecko and [].join() in others
58391         if(Roo.isGecko){
58392             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58393                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58394                     "';};};";
58395         }else{
58396             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58397             body.push(tpl.body.replace(/(\r\n|\n)/g,
58398                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58399             body.push("'].join('');};};");
58400             body = body.join('');
58401         }
58402         
58403         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58404        
58405         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58406         eval(body);
58407         
58408         return this;
58409     },
58410
58411     applyTemplate : function(values){
58412         return this.master.compiled.call(this, values, {});
58413         //var s = this.subs;
58414     },
58415
58416     apply : function(){
58417         return this.applyTemplate.apply(this, arguments);
58418     }
58419
58420  });
58421
58422 Roo.XTemplate.from = function(el){
58423     el = Roo.getDom(el);
58424     return new Roo.XTemplate(el.value || el.innerHTML);
58425 };