Roo/View.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         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1241         + "if (results && results.length > 0) {";
1242     var regex = "";
1243
1244     var special = false;
1245     var ch = '';
1246     for (var i = 0; i < format.length; ++i) {
1247         ch = format.charAt(i);
1248         if (!special && ch == "\\") {
1249             special = true;
1250         }
1251         else if (special) {
1252             special = false;
1253             regex += String.escape(ch);
1254         }
1255         else {
1256             var obj = Date.formatCodeToRegex(ch, currentGroup);
1257             currentGroup += obj.g;
1258             regex += obj.s;
1259             if (obj.g && obj.c) {
1260                 code += obj.c;
1261             }
1262         }
1263     }
1264
1265     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1266         + "{v = new Date(y, m, d, h, i, s);}\n"
1267         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1268         + "{v = new Date(y, m, d, h, i);}\n"
1269         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1270         + "{v = new Date(y, m, d, h);}\n"
1271         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1272         + "{v = new Date(y, m, d);}\n"
1273         + "else if (y >= 0 && m >= 0)\n"
1274         + "{v = new Date(y, m);}\n"
1275         + "else if (y >= 0)\n"
1276         + "{v = new Date(y);}\n"
1277         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1278         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1279         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1280         + ";}";
1281
1282     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1283     /** eval:var:zzzzzzzzzzzzz */
1284     eval(code);
1285 };
1286
1287 // private
1288 Date.formatCodeToRegex = function(character, currentGroup) {
1289     switch (character) {
1290     case "D":
1291         return {g:0,
1292         c:null,
1293         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1294     case "j":
1295         return {g:1,
1296             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1297             s:"(\\d{1,2})"}; // day of month without leading zeroes
1298     case "d":
1299         return {g:1,
1300             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1301             s:"(\\d{2})"}; // day of month with leading zeroes
1302     case "l":
1303         return {g:0,
1304             c:null,
1305             s:"(?:" + Date.dayNames.join("|") + ")"};
1306     case "S":
1307         return {g:0,
1308             c:null,
1309             s:"(?:st|nd|rd|th)"};
1310     case "w":
1311         return {g:0,
1312             c:null,
1313             s:"\\d"};
1314     case "z":
1315         return {g:0,
1316             c:null,
1317             s:"(?:\\d{1,3})"};
1318     case "W":
1319         return {g:0,
1320             c:null,
1321             s:"(?:\\d{2})"};
1322     case "F":
1323         return {g:1,
1324             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1325             s:"(" + Date.monthNames.join("|") + ")"};
1326     case "M":
1327         return {g:1,
1328             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1329             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1330     case "n":
1331         return {g:1,
1332             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1333             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1334     case "m":
1335         return {g:1,
1336             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1337             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1338     case "t":
1339         return {g:0,
1340             c:null,
1341             s:"\\d{1,2}"};
1342     case "L":
1343         return {g:0,
1344             c:null,
1345             s:"(?:1|0)"};
1346     case "Y":
1347         return {g:1,
1348             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1349             s:"(\\d{4})"};
1350     case "y":
1351         return {g:1,
1352             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1353                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1354             s:"(\\d{1,2})"};
1355     case "a":
1356         return {g:1,
1357             c:"if (results[" + currentGroup + "] == 'am') {\n"
1358                 + "if (h == 12) { h = 0; }\n"
1359                 + "} else { if (h < 12) { h += 12; }}",
1360             s:"(am|pm)"};
1361     case "A":
1362         return {g:1,
1363             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1364                 + "if (h == 12) { h = 0; }\n"
1365                 + "} else { if (h < 12) { h += 12; }}",
1366             s:"(AM|PM)"};
1367     case "g":
1368     case "G":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1372     case "h":
1373     case "H":
1374         return {g:1,
1375             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1377     case "i":
1378         return {g:1,
1379             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1380             s:"(\\d{2})"};
1381     case "s":
1382         return {g:1,
1383             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{2})"};
1385     case "O":
1386         return {g:1,
1387             c:[
1388                 "o = results[", currentGroup, "];\n",
1389                 "var sn = o.substring(0,1);\n", // get + / - sign
1390                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1391                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1392                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1393                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1394             ].join(""),
1395             s:"([+\-]\\d{2,4})"};
1396     
1397     
1398     case "P":
1399         return {g:1,
1400                 c:[
1401                    "o = results[", currentGroup, "];\n",
1402                    "var sn = o.substring(0,1);\n",
1403                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1404                    "var mn = o.substring(4,6) % 60;\n",
1405                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1406                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1407             ].join(""),
1408             s:"([+\-]\\d{4})"};
1409     case "T":
1410         return {g:0,
1411             c:null,
1412             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1413     case "Z":
1414         return {g:1,
1415             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1416                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1417             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1418     default:
1419         return {g:0,
1420             c:null,
1421             s:String.escape(character)};
1422     }
1423 };
1424
1425 /**
1426  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1427  * @return {String} The abbreviated timezone name (e.g. 'CST')
1428  */
1429 Date.prototype.getTimezone = function() {
1430     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1431 };
1432
1433 /**
1434  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1435  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1436  */
1437 Date.prototype.getGMTOffset = function() {
1438     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1439         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1440         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1441 };
1442
1443 /**
1444  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1445  * @return {String} 2-characters representing hours and 2-characters representing minutes
1446  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1447  */
1448 Date.prototype.getGMTColonOffset = function() {
1449         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1450                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1451                 + ":"
1452                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1453 }
1454
1455 /**
1456  * Get the numeric day number of the year, adjusted for leap year.
1457  * @return {Number} 0 through 364 (365 in leap years)
1458  */
1459 Date.prototype.getDayOfYear = function() {
1460     var num = 0;
1461     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1462     for (var i = 0; i < this.getMonth(); ++i) {
1463         num += Date.daysInMonth[i];
1464     }
1465     return num + this.getDate() - 1;
1466 };
1467
1468 /**
1469  * Get the string representation of the numeric week number of the year
1470  * (equivalent to the format specifier 'W').
1471  * @return {String} '00' through '52'
1472  */
1473 Date.prototype.getWeekOfYear = function() {
1474     // Skip to Thursday of this week
1475     var now = this.getDayOfYear() + (4 - this.getDay());
1476     // Find the first Thursday of the year
1477     var jan1 = new Date(this.getFullYear(), 0, 1);
1478     var then = (7 - jan1.getDay() + 4);
1479     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1480 };
1481
1482 /**
1483  * Whether or not the current date is in a leap year.
1484  * @return {Boolean} True if the current date is in a leap year, else false
1485  */
1486 Date.prototype.isLeapYear = function() {
1487     var year = this.getFullYear();
1488     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1489 };
1490
1491 /**
1492  * Get the first day of the current month, adjusted for leap year.  The returned value
1493  * is the numeric day index within the week (0-6) which can be used in conjunction with
1494  * the {@link #monthNames} array to retrieve the textual day name.
1495  * Example:
1496  *<pre><code>
1497 var dt = new Date('1/10/2007');
1498 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1499 </code></pre>
1500  * @return {Number} The day number (0-6)
1501  */
1502 Date.prototype.getFirstDayOfMonth = function() {
1503     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1504     return (day < 0) ? (day + 7) : day;
1505 };
1506
1507 /**
1508  * Get the last day of the current month, adjusted for leap year.  The returned value
1509  * is the numeric day index within the week (0-6) which can be used in conjunction with
1510  * the {@link #monthNames} array to retrieve the textual day name.
1511  * Example:
1512  *<pre><code>
1513 var dt = new Date('1/10/2007');
1514 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1515 </code></pre>
1516  * @return {Number} The day number (0-6)
1517  */
1518 Date.prototype.getLastDayOfMonth = function() {
1519     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1520     return (day < 0) ? (day + 7) : day;
1521 };
1522
1523
1524 /**
1525  * Get the first date of this date's month
1526  * @return {Date}
1527  */
1528 Date.prototype.getFirstDateOfMonth = function() {
1529     return new Date(this.getFullYear(), this.getMonth(), 1);
1530 };
1531
1532 /**
1533  * Get the last date of this date's month
1534  * @return {Date}
1535  */
1536 Date.prototype.getLastDateOfMonth = function() {
1537     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1538 };
1539 /**
1540  * Get the number of days in the current month, adjusted for leap year.
1541  * @return {Number} The number of days in the month
1542  */
1543 Date.prototype.getDaysInMonth = function() {
1544     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1545     return Date.daysInMonth[this.getMonth()];
1546 };
1547
1548 /**
1549  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1550  * @return {String} 'st, 'nd', 'rd' or 'th'
1551  */
1552 Date.prototype.getSuffix = function() {
1553     switch (this.getDate()) {
1554         case 1:
1555         case 21:
1556         case 31:
1557             return "st";
1558         case 2:
1559         case 22:
1560             return "nd";
1561         case 3:
1562         case 23:
1563             return "rd";
1564         default:
1565             return "th";
1566     }
1567 };
1568
1569 // private
1570 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1571
1572 /**
1573  * An array of textual month names.
1574  * Override these values for international dates, for example...
1575  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1576  * @type Array
1577  * @static
1578  */
1579 Date.monthNames =
1580    ["January",
1581     "February",
1582     "March",
1583     "April",
1584     "May",
1585     "June",
1586     "July",
1587     "August",
1588     "September",
1589     "October",
1590     "November",
1591     "December"];
1592
1593 /**
1594  * An array of textual day names.
1595  * Override these values for international dates, for example...
1596  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1597  * @type Array
1598  * @static
1599  */
1600 Date.dayNames =
1601    ["Sunday",
1602     "Monday",
1603     "Tuesday",
1604     "Wednesday",
1605     "Thursday",
1606     "Friday",
1607     "Saturday"];
1608
1609 // private
1610 Date.y2kYear = 50;
1611 // private
1612 Date.monthNumbers = {
1613     Jan:0,
1614     Feb:1,
1615     Mar:2,
1616     Apr:3,
1617     May:4,
1618     Jun:5,
1619     Jul:6,
1620     Aug:7,
1621     Sep:8,
1622     Oct:9,
1623     Nov:10,
1624     Dec:11};
1625
1626 /**
1627  * Creates and returns a new Date instance with the exact same date value as the called instance.
1628  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1629  * variable will also be changed.  When the intention is to create a new variable that will not
1630  * modify the original instance, you should create a clone.
1631  *
1632  * Example of correctly cloning a date:
1633  * <pre><code>
1634 //wrong way:
1635 var orig = new Date('10/1/2006');
1636 var copy = orig;
1637 copy.setDate(5);
1638 document.write(orig);  //returns 'Thu Oct 05 2006'!
1639
1640 //correct way:
1641 var orig = new Date('10/1/2006');
1642 var copy = orig.clone();
1643 copy.setDate(5);
1644 document.write(orig);  //returns 'Thu Oct 01 2006'
1645 </code></pre>
1646  * @return {Date} The new Date instance
1647  */
1648 Date.prototype.clone = function() {
1649         return new Date(this.getTime());
1650 };
1651
1652 /**
1653  * Clears any time information from this date
1654  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1655  @return {Date} this or the clone
1656  */
1657 Date.prototype.clearTime = function(clone){
1658     if(clone){
1659         return this.clone().clearTime();
1660     }
1661     this.setHours(0);
1662     this.setMinutes(0);
1663     this.setSeconds(0);
1664     this.setMilliseconds(0);
1665     return this;
1666 };
1667
1668 // private
1669 // safari setMonth is broken
1670 if(Roo.isSafari){
1671     Date.brokenSetMonth = Date.prototype.setMonth;
1672         Date.prototype.setMonth = function(num){
1673                 if(num <= -1){
1674                         var n = Math.ceil(-num);
1675                         var back_year = Math.ceil(n/12);
1676                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1677                         this.setFullYear(this.getFullYear() - back_year);
1678                         return Date.brokenSetMonth.call(this, month);
1679                 } else {
1680                         return Date.brokenSetMonth.apply(this, arguments);
1681                 }
1682         };
1683 }
1684
1685 /** Date interval constant 
1686 * @static 
1687 * @type String */
1688 Date.MILLI = "ms";
1689 /** Date interval constant 
1690 * @static 
1691 * @type String */
1692 Date.SECOND = "s";
1693 /** Date interval constant 
1694 * @static 
1695 * @type String */
1696 Date.MINUTE = "mi";
1697 /** Date interval constant 
1698 * @static 
1699 * @type String */
1700 Date.HOUR = "h";
1701 /** Date interval constant 
1702 * @static 
1703 * @type String */
1704 Date.DAY = "d";
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MONTH = "mo";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.YEAR = "y";
1713
1714 /**
1715  * Provides a convenient method of performing basic date arithmetic.  This method
1716  * does not modify the Date instance being called - it creates and returns
1717  * a new Date instance containing the resulting date value.
1718  *
1719  * Examples:
1720  * <pre><code>
1721 //Basic usage:
1722 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1723 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1724
1725 //Negative values will subtract correctly:
1726 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1727 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1728
1729 //You can even chain several calls together in one line!
1730 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1731 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1732  </code></pre>
1733  *
1734  * @param {String} interval   A valid date interval enum value
1735  * @param {Number} value      The amount to add to the current date
1736  * @return {Date} The new Date instance
1737  */
1738 Date.prototype.add = function(interval, value){
1739   var d = this.clone();
1740   if (!interval || value === 0) return d;
1741   switch(interval.toLowerCase()){
1742     case Date.MILLI:
1743       d.setMilliseconds(this.getMilliseconds() + value);
1744       break;
1745     case Date.SECOND:
1746       d.setSeconds(this.getSeconds() + value);
1747       break;
1748     case Date.MINUTE:
1749       d.setMinutes(this.getMinutes() + value);
1750       break;
1751     case Date.HOUR:
1752       d.setHours(this.getHours() + value);
1753       break;
1754     case Date.DAY:
1755       d.setDate(this.getDate() + value);
1756       break;
1757     case Date.MONTH:
1758       var day = this.getDate();
1759       if(day > 28){
1760           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1761       }
1762       d.setDate(day);
1763       d.setMonth(this.getMonth() + value);
1764       break;
1765     case Date.YEAR:
1766       d.setFullYear(this.getFullYear() + value);
1767       break;
1768   }
1769   return d;
1770 };
1771 /*
1772  * Based on:
1773  * Ext JS Library 1.1.1
1774  * Copyright(c) 2006-2007, Ext JS, LLC.
1775  *
1776  * Originally Released Under LGPL - original licence link has changed is not relivant.
1777  *
1778  * Fork - LGPL
1779  * <script type="text/javascript">
1780  */
1781
1782 /**
1783  * @class Roo.lib.Dom
1784  * @static
1785  * 
1786  * Dom utils (from YIU afaik)
1787  * 
1788  **/
1789 Roo.lib.Dom = {
1790     /**
1791      * Get the view width
1792      * @param {Boolean} full True will get the full document, otherwise it's the view width
1793      * @return {Number} The width
1794      */
1795      
1796     getViewWidth : function(full) {
1797         return full ? this.getDocumentWidth() : this.getViewportWidth();
1798     },
1799     /**
1800      * Get the view height
1801      * @param {Boolean} full True will get the full document, otherwise it's the view height
1802      * @return {Number} The height
1803      */
1804     getViewHeight : function(full) {
1805         return full ? this.getDocumentHeight() : this.getViewportHeight();
1806     },
1807
1808     getDocumentHeight: function() {
1809         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1810         return Math.max(scrollHeight, this.getViewportHeight());
1811     },
1812
1813     getDocumentWidth: function() {
1814         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1815         return Math.max(scrollWidth, this.getViewportWidth());
1816     },
1817
1818     getViewportHeight: function() {
1819         var height = self.innerHeight;
1820         var mode = document.compatMode;
1821
1822         if ((mode || Roo.isIE) && !Roo.isOpera) {
1823             height = (mode == "CSS1Compat") ?
1824                      document.documentElement.clientHeight :
1825                      document.body.clientHeight;
1826         }
1827
1828         return height;
1829     },
1830
1831     getViewportWidth: function() {
1832         var width = self.innerWidth;
1833         var mode = document.compatMode;
1834
1835         if (mode || Roo.isIE) {
1836             width = (mode == "CSS1Compat") ?
1837                     document.documentElement.clientWidth :
1838                     document.body.clientWidth;
1839         }
1840         return width;
1841     },
1842
1843     isAncestor : function(p, c) {
1844         p = Roo.getDom(p);
1845         c = Roo.getDom(c);
1846         if (!p || !c) {
1847             return false;
1848         }
1849
1850         if (p.contains && !Roo.isSafari) {
1851             return p.contains(c);
1852         } else if (p.compareDocumentPosition) {
1853             return !!(p.compareDocumentPosition(c) & 16);
1854         } else {
1855             var parent = c.parentNode;
1856             while (parent) {
1857                 if (parent == p) {
1858                     return true;
1859                 }
1860                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1861                     return false;
1862                 }
1863                 parent = parent.parentNode;
1864             }
1865             return false;
1866         }
1867     },
1868
1869     getRegion : function(el) {
1870         return Roo.lib.Region.getRegion(el);
1871     },
1872
1873     getY : function(el) {
1874         return this.getXY(el)[1];
1875     },
1876
1877     getX : function(el) {
1878         return this.getXY(el)[0];
1879     },
1880
1881     getXY : function(el) {
1882         var p, pe, b, scroll, bd = document.body;
1883         el = Roo.getDom(el);
1884         var fly = Roo.lib.AnimBase.fly;
1885         if (el.getBoundingClientRect) {
1886             b = el.getBoundingClientRect();
1887             scroll = fly(document).getScroll();
1888             return [b.left + scroll.left, b.top + scroll.top];
1889         }
1890         var x = 0, y = 0;
1891
1892         p = el;
1893
1894         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1895
1896         while (p) {
1897
1898             x += p.offsetLeft;
1899             y += p.offsetTop;
1900
1901             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1902                 hasAbsolute = true;
1903             }
1904
1905             if (Roo.isGecko) {
1906                 pe = fly(p);
1907
1908                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1909                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1910
1911
1912                 x += bl;
1913                 y += bt;
1914
1915
1916                 if (p != el && pe.getStyle('overflow') != 'visible') {
1917                     x += bl;
1918                     y += bt;
1919                 }
1920             }
1921             p = p.offsetParent;
1922         }
1923
1924         if (Roo.isSafari && hasAbsolute) {
1925             x -= bd.offsetLeft;
1926             y -= bd.offsetTop;
1927         }
1928
1929         if (Roo.isGecko && !hasAbsolute) {
1930             var dbd = fly(bd);
1931             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1932             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1933         }
1934
1935         p = el.parentNode;
1936         while (p && p != bd) {
1937             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1938                 x -= p.scrollLeft;
1939                 y -= p.scrollTop;
1940             }
1941             p = p.parentNode;
1942         }
1943         return [x, y];
1944     },
1945  
1946   
1947
1948
1949     setXY : function(el, xy) {
1950         el = Roo.fly(el, '_setXY');
1951         el.position();
1952         var pts = el.translatePoints(xy);
1953         if (xy[0] !== false) {
1954             el.dom.style.left = pts.left + "px";
1955         }
1956         if (xy[1] !== false) {
1957             el.dom.style.top = pts.top + "px";
1958         }
1959     },
1960
1961     setX : function(el, x) {
1962         this.setXY(el, [x, false]);
1963     },
1964
1965     setY : function(el, y) {
1966         this.setXY(el, [false, y]);
1967     }
1968 };
1969 /*
1970  * Portions of this file are based on pieces of Yahoo User Interface Library
1971  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1972  * YUI licensed under the BSD License:
1973  * http://developer.yahoo.net/yui/license.txt
1974  * <script type="text/javascript">
1975  *
1976  */
1977
1978 Roo.lib.Event = function() {
1979     var loadComplete = false;
1980     var listeners = [];
1981     var unloadListeners = [];
1982     var retryCount = 0;
1983     var onAvailStack = [];
1984     var counter = 0;
1985     var lastError = null;
1986
1987     return {
1988         POLL_RETRYS: 200,
1989         POLL_INTERVAL: 20,
1990         EL: 0,
1991         TYPE: 1,
1992         FN: 2,
1993         WFN: 3,
1994         OBJ: 3,
1995         ADJ_SCOPE: 4,
1996         _interval: null,
1997
1998         startInterval: function() {
1999             if (!this._interval) {
2000                 var self = this;
2001                 var callback = function() {
2002                     self._tryPreloadAttach();
2003                 };
2004                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2005
2006             }
2007         },
2008
2009         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2010             onAvailStack.push({ id:         p_id,
2011                 fn:         p_fn,
2012                 obj:        p_obj,
2013                 override:   p_override,
2014                 checkReady: false    });
2015
2016             retryCount = this.POLL_RETRYS;
2017             this.startInterval();
2018         },
2019
2020
2021         addListener: function(el, eventName, fn) {
2022             el = Roo.getDom(el);
2023             if (!el || !fn) {
2024                 return false;
2025             }
2026
2027             if ("unload" == eventName) {
2028                 unloadListeners[unloadListeners.length] =
2029                 [el, eventName, fn];
2030                 return true;
2031             }
2032
2033             var wrappedFn = function(e) {
2034                 return fn(Roo.lib.Event.getEvent(e));
2035             };
2036
2037             var li = [el, eventName, fn, wrappedFn];
2038
2039             var index = listeners.length;
2040             listeners[index] = li;
2041
2042             this.doAdd(el, eventName, wrappedFn, false);
2043             return true;
2044
2045         },
2046
2047
2048         removeListener: function(el, eventName, fn) {
2049             var i, len;
2050
2051             el = Roo.getDom(el);
2052
2053             if(!fn) {
2054                 return this.purgeElement(el, false, eventName);
2055             }
2056
2057
2058             if ("unload" == eventName) {
2059
2060                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2061                     var li = unloadListeners[i];
2062                     if (li &&
2063                         li[0] == el &&
2064                         li[1] == eventName &&
2065                         li[2] == fn) {
2066                         unloadListeners.splice(i, 1);
2067                         return true;
2068                     }
2069                 }
2070
2071                 return false;
2072             }
2073
2074             var cacheItem = null;
2075
2076
2077             var index = arguments[3];
2078
2079             if ("undefined" == typeof index) {
2080                 index = this._getCacheIndex(el, eventName, fn);
2081             }
2082
2083             if (index >= 0) {
2084                 cacheItem = listeners[index];
2085             }
2086
2087             if (!el || !cacheItem) {
2088                 return false;
2089             }
2090
2091             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2092
2093             delete listeners[index][this.WFN];
2094             delete listeners[index][this.FN];
2095             listeners.splice(index, 1);
2096
2097             return true;
2098
2099         },
2100
2101
2102         getTarget: function(ev, resolveTextNode) {
2103             ev = ev.browserEvent || ev;
2104             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2105             var t = ev.target || ev.srcElement;
2106             return this.resolveTextNode(t);
2107         },
2108
2109
2110         resolveTextNode: function(node) {
2111             if (Roo.isSafari && node && 3 == node.nodeType) {
2112                 return node.parentNode;
2113             } else {
2114                 return node;
2115             }
2116         },
2117
2118
2119         getPageX: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var x = ev.pageX;
2123             if (!x && 0 !== x) {
2124                 x = ev.clientX || 0;
2125
2126                 if (Roo.isIE) {
2127                     x += this.getScroll()[1];
2128                 }
2129             }
2130
2131             return x;
2132         },
2133
2134
2135         getPageY: function(ev) {
2136             ev = ev.browserEvent || ev;
2137             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2138             var y = ev.pageY;
2139             if (!y && 0 !== y) {
2140                 y = ev.clientY || 0;
2141
2142                 if (Roo.isIE) {
2143                     y += this.getScroll()[0];
2144                 }
2145             }
2146
2147
2148             return y;
2149         },
2150
2151
2152         getXY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             return [this.getPageX(ev), this.getPageY(ev)];
2156         },
2157
2158
2159         getRelatedTarget: function(ev) {
2160             ev = ev.browserEvent || ev;
2161             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2162             var t = ev.relatedTarget;
2163             if (!t) {
2164                 if (ev.type == "mouseout") {
2165                     t = ev.toElement;
2166                 } else if (ev.type == "mouseover") {
2167                     t = ev.fromElement;
2168                 }
2169             }
2170
2171             return this.resolveTextNode(t);
2172         },
2173
2174
2175         getTime: function(ev) {
2176             ev = ev.browserEvent || ev;
2177             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2178             if (!ev.time) {
2179                 var t = new Date().getTime();
2180                 try {
2181                     ev.time = t;
2182                 } catch(ex) {
2183                     this.lastError = ex;
2184                     return t;
2185                 }
2186             }
2187
2188             return ev.time;
2189         },
2190
2191
2192         stopEvent: function(ev) {
2193             this.stopPropagation(ev);
2194             this.preventDefault(ev);
2195         },
2196
2197
2198         stopPropagation: function(ev) {
2199             ev = ev.browserEvent || ev;
2200             if (ev.stopPropagation) {
2201                 ev.stopPropagation();
2202             } else {
2203                 ev.cancelBubble = true;
2204             }
2205         },
2206
2207
2208         preventDefault: function(ev) {
2209             ev = ev.browserEvent || ev;
2210             if(ev.preventDefault) {
2211                 ev.preventDefault();
2212             } else {
2213                 ev.returnValue = false;
2214             }
2215         },
2216
2217
2218         getEvent: function(e) {
2219             var ev = e || window.event;
2220             if (!ev) {
2221                 var c = this.getEvent.caller;
2222                 while (c) {
2223                     ev = c.arguments[0];
2224                     if (ev && Event == ev.constructor) {
2225                         break;
2226                     }
2227                     c = c.caller;
2228                 }
2229             }
2230             return ev;
2231         },
2232
2233
2234         getCharCode: function(ev) {
2235             ev = ev.browserEvent || ev;
2236             return ev.charCode || ev.keyCode || 0;
2237         },
2238
2239
2240         _getCacheIndex: function(el, eventName, fn) {
2241             for (var i = 0,len = listeners.length; i < len; ++i) {
2242                 var li = listeners[i];
2243                 if (li &&
2244                     li[this.FN] == fn &&
2245                     li[this.EL] == el &&
2246                     li[this.TYPE] == eventName) {
2247                     return i;
2248                 }
2249             }
2250
2251             return -1;
2252         },
2253
2254
2255         elCache: {},
2256
2257
2258         getEl: function(id) {
2259             return document.getElementById(id);
2260         },
2261
2262
2263         clearCache: function() {
2264         },
2265
2266
2267         _load: function(e) {
2268             loadComplete = true;
2269             var EU = Roo.lib.Event;
2270
2271
2272             if (Roo.isIE) {
2273                 EU.doRemove(window, "load", EU._load);
2274             }
2275         },
2276
2277
2278         _tryPreloadAttach: function() {
2279
2280             if (this.locked) {
2281                 return false;
2282             }
2283
2284             this.locked = true;
2285
2286
2287             var tryAgain = !loadComplete;
2288             if (!tryAgain) {
2289                 tryAgain = (retryCount > 0);
2290             }
2291
2292
2293             var notAvail = [];
2294             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2295                 var item = onAvailStack[i];
2296                 if (item) {
2297                     var el = this.getEl(item.id);
2298
2299                     if (el) {
2300                         if (!item.checkReady ||
2301                             loadComplete ||
2302                             el.nextSibling ||
2303                             (document && document.body)) {
2304
2305                             var scope = el;
2306                             if (item.override) {
2307                                 if (item.override === true) {
2308                                     scope = item.obj;
2309                                 } else {
2310                                     scope = item.override;
2311                                 }
2312                             }
2313                             item.fn.call(scope, item.obj);
2314                             onAvailStack[i] = null;
2315                         }
2316                     } else {
2317                         notAvail.push(item);
2318                     }
2319                 }
2320             }
2321
2322             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2323
2324             if (tryAgain) {
2325
2326                 this.startInterval();
2327             } else {
2328                 clearInterval(this._interval);
2329                 this._interval = null;
2330             }
2331
2332             this.locked = false;
2333
2334             return true;
2335
2336         },
2337
2338
2339         purgeElement: function(el, recurse, eventName) {
2340             var elListeners = this.getListeners(el, eventName);
2341             if (elListeners) {
2342                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2343                     var l = elListeners[i];
2344                     this.removeListener(el, l.type, l.fn);
2345                 }
2346             }
2347
2348             if (recurse && el && el.childNodes) {
2349                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2350                     this.purgeElement(el.childNodes[i], recurse, eventName);
2351                 }
2352             }
2353         },
2354
2355
2356         getListeners: function(el, eventName) {
2357             var results = [], searchLists;
2358             if (!eventName) {
2359                 searchLists = [listeners, unloadListeners];
2360             } else if (eventName == "unload") {
2361                 searchLists = [unloadListeners];
2362             } else {
2363                 searchLists = [listeners];
2364             }
2365
2366             for (var j = 0; j < searchLists.length; ++j) {
2367                 var searchList = searchLists[j];
2368                 if (searchList && searchList.length > 0) {
2369                     for (var i = 0,len = searchList.length; i < len; ++i) {
2370                         var l = searchList[i];
2371                         if (l && l[this.EL] === el &&
2372                             (!eventName || eventName === l[this.TYPE])) {
2373                             results.push({
2374                                 type:   l[this.TYPE],
2375                                 fn:     l[this.FN],
2376                                 obj:    l[this.OBJ],
2377                                 adjust: l[this.ADJ_SCOPE],
2378                                 index:  i
2379                             });
2380                         }
2381                     }
2382                 }
2383             }
2384
2385             return (results.length) ? results : null;
2386         },
2387
2388
2389         _unload: function(e) {
2390
2391             var EU = Roo.lib.Event, i, j, l, len, index;
2392
2393             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2394                 l = unloadListeners[i];
2395                 if (l) {
2396                     var scope = window;
2397                     if (l[EU.ADJ_SCOPE]) {
2398                         if (l[EU.ADJ_SCOPE] === true) {
2399                             scope = l[EU.OBJ];
2400                         } else {
2401                             scope = l[EU.ADJ_SCOPE];
2402                         }
2403                     }
2404                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2405                     unloadListeners[i] = null;
2406                     l = null;
2407                     scope = null;
2408                 }
2409             }
2410
2411             unloadListeners = null;
2412
2413             if (listeners && listeners.length > 0) {
2414                 j = listeners.length;
2415                 while (j) {
2416                     index = j - 1;
2417                     l = listeners[index];
2418                     if (l) {
2419                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2420                                 l[EU.FN], index);
2421                     }
2422                     j = j - 1;
2423                 }
2424                 l = null;
2425
2426                 EU.clearCache();
2427             }
2428
2429             EU.doRemove(window, "unload", EU._unload);
2430
2431         },
2432
2433
2434         getScroll: function() {
2435             var dd = document.documentElement, db = document.body;
2436             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2437                 return [dd.scrollTop, dd.scrollLeft];
2438             } else if (db) {
2439                 return [db.scrollTop, db.scrollLeft];
2440             } else {
2441                 return [0, 0];
2442             }
2443         },
2444
2445
2446         doAdd: function () {
2447             if (window.addEventListener) {
2448                 return function(el, eventName, fn, capture) {
2449                     el.addEventListener(eventName, fn, (capture));
2450                 };
2451             } else if (window.attachEvent) {
2452                 return function(el, eventName, fn, capture) {
2453                     el.attachEvent("on" + eventName, fn);
2454                 };
2455             } else {
2456                 return function() {
2457                 };
2458             }
2459         }(),
2460
2461
2462         doRemove: function() {
2463             if (window.removeEventListener) {
2464                 return function (el, eventName, fn, capture) {
2465                     el.removeEventListener(eventName, fn, (capture));
2466                 };
2467             } else if (window.detachEvent) {
2468                 return function (el, eventName, fn) {
2469                     el.detachEvent("on" + eventName, fn);
2470                 };
2471             } else {
2472                 return function() {
2473                 };
2474             }
2475         }()
2476     };
2477     
2478 }();
2479 (function() {     
2480    
2481     var E = Roo.lib.Event;
2482     E.on = E.addListener;
2483     E.un = E.removeListener;
2484
2485     if (document && document.body) {
2486         E._load();
2487     } else {
2488         E.doAdd(window, "load", E._load);
2489     }
2490     E.doAdd(window, "unload", E._unload);
2491     E._tryPreloadAttach();
2492 })();
2493
2494 /*
2495  * Portions of this file are based on pieces of Yahoo User Interface Library
2496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2497  * YUI licensed under the BSD License:
2498  * http://developer.yahoo.net/yui/license.txt
2499  * <script type="text/javascript">
2500  *
2501  */
2502
2503 (function() {
2504     /**
2505      * @class Roo.lib.Ajax
2506      *
2507      */
2508     Roo.lib.Ajax = {
2509         /**
2510          * @static 
2511          */
2512         request : function(method, uri, cb, data, options) {
2513             if(options){
2514                 var hs = options.headers;
2515                 if(hs){
2516                     for(var h in hs){
2517                         if(hs.hasOwnProperty(h)){
2518                             this.initHeader(h, hs[h], false);
2519                         }
2520                     }
2521                 }
2522                 if(options.xmlData){
2523                     this.initHeader('Content-Type', 'text/xml', false);
2524                     method = 'POST';
2525                     data = options.xmlData;
2526                 }
2527             }
2528
2529             return this.asyncRequest(method, uri, cb, data);
2530         },
2531
2532         serializeForm : function(form) {
2533             if(typeof form == 'string') {
2534                 form = (document.getElementById(form) || document.forms[form]);
2535             }
2536
2537             var el, name, val, disabled, data = '', hasSubmit = false;
2538             for (var i = 0; i < form.elements.length; i++) {
2539                 el = form.elements[i];
2540                 disabled = form.elements[i].disabled;
2541                 name = form.elements[i].name;
2542                 val = form.elements[i].value;
2543
2544                 if (!disabled && name){
2545                     switch (el.type)
2546                             {
2547                         case 'select-one':
2548                         case 'select-multiple':
2549                             for (var j = 0; j < el.options.length; j++) {
2550                                 if (el.options[j].selected) {
2551                                     if (Roo.isIE) {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                     else {
2555                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2556                                     }
2557                                 }
2558                             }
2559                             break;
2560                         case 'radio':
2561                         case 'checkbox':
2562                             if (el.checked) {
2563                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2564                             }
2565                             break;
2566                         case 'file':
2567
2568                         case undefined:
2569
2570                         case 'reset':
2571
2572                         case 'button':
2573
2574                             break;
2575                         case 'submit':
2576                             if(hasSubmit == false) {
2577                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2578                                 hasSubmit = true;
2579                             }
2580                             break;
2581                         default:
2582                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2583                             break;
2584                     }
2585                 }
2586             }
2587             data = data.substr(0, data.length - 1);
2588             return data;
2589         },
2590
2591         headers:{},
2592
2593         hasHeaders:false,
2594
2595         useDefaultHeader:true,
2596
2597         defaultPostHeader:'application/x-www-form-urlencoded',
2598
2599         useDefaultXhrHeader:true,
2600
2601         defaultXhrHeader:'XMLHttpRequest',
2602
2603         hasDefaultHeaders:true,
2604
2605         defaultHeaders:{},
2606
2607         poll:{},
2608
2609         timeout:{},
2610
2611         pollInterval:50,
2612
2613         transactionId:0,
2614
2615         setProgId:function(id)
2616         {
2617             this.activeX.unshift(id);
2618         },
2619
2620         setDefaultPostHeader:function(b)
2621         {
2622             this.useDefaultHeader = b;
2623         },
2624
2625         setDefaultXhrHeader:function(b)
2626         {
2627             this.useDefaultXhrHeader = b;
2628         },
2629
2630         setPollingInterval:function(i)
2631         {
2632             if (typeof i == 'number' && isFinite(i)) {
2633                 this.pollInterval = i;
2634             }
2635         },
2636
2637         createXhrObject:function(transactionId)
2638         {
2639             var obj,http;
2640             try
2641             {
2642
2643                 http = new XMLHttpRequest();
2644
2645                 obj = { conn:http, tId:transactionId };
2646             }
2647             catch(e)
2648             {
2649                 for (var i = 0; i < this.activeX.length; ++i) {
2650                     try
2651                     {
2652
2653                         http = new ActiveXObject(this.activeX[i]);
2654
2655                         obj = { conn:http, tId:transactionId };
2656                         break;
2657                     }
2658                     catch(e) {
2659                     }
2660                 }
2661             }
2662             finally
2663             {
2664                 return obj;
2665             }
2666         },
2667
2668         getConnectionObject:function()
2669         {
2670             var o;
2671             var tId = this.transactionId;
2672
2673             try
2674             {
2675                 o = this.createXhrObject(tId);
2676                 if (o) {
2677                     this.transactionId++;
2678                 }
2679             }
2680             catch(e) {
2681             }
2682             finally
2683             {
2684                 return o;
2685             }
2686         },
2687
2688         asyncRequest:function(method, uri, callback, postData)
2689         {
2690             var o = this.getConnectionObject();
2691
2692             if (!o) {
2693                 return null;
2694             }
2695             else {
2696                 o.conn.open(method, uri, true);
2697
2698                 if (this.useDefaultXhrHeader) {
2699                     if (!this.defaultHeaders['X-Requested-With']) {
2700                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2701                     }
2702                 }
2703
2704                 if(postData && this.useDefaultHeader){
2705                     this.initHeader('Content-Type', this.defaultPostHeader);
2706                 }
2707
2708                  if (this.hasDefaultHeaders || this.hasHeaders) {
2709                     this.setHeader(o);
2710                 }
2711
2712                 this.handleReadyState(o, callback);
2713                 o.conn.send(postData || null);
2714
2715                 return o;
2716             }
2717         },
2718
2719         handleReadyState:function(o, callback)
2720         {
2721             var oConn = this;
2722
2723             if (callback && callback.timeout) {
2724                 
2725                 this.timeout[o.tId] = window.setTimeout(function() {
2726                     oConn.abort(o, callback, true);
2727                 }, callback.timeout);
2728             }
2729
2730             this.poll[o.tId] = window.setInterval(
2731                     function() {
2732                         if (o.conn && o.conn.readyState == 4) {
2733                             window.clearInterval(oConn.poll[o.tId]);
2734                             delete oConn.poll[o.tId];
2735
2736                             if(callback && callback.timeout) {
2737                                 window.clearTimeout(oConn.timeout[o.tId]);
2738                                 delete oConn.timeout[o.tId];
2739                             }
2740
2741                             oConn.handleTransactionResponse(o, callback);
2742                         }
2743                     }
2744                     , this.pollInterval);
2745         },
2746
2747         handleTransactionResponse:function(o, callback, isAbort)
2748         {
2749
2750             if (!callback) {
2751                 this.releaseObject(o);
2752                 return;
2753             }
2754
2755             var httpStatus, responseObject;
2756
2757             try
2758             {
2759                 if (o.conn.status !== undefined && o.conn.status != 0) {
2760                     httpStatus = o.conn.status;
2761                 }
2762                 else {
2763                     httpStatus = 13030;
2764                 }
2765             }
2766             catch(e) {
2767
2768
2769                 httpStatus = 13030;
2770             }
2771
2772             if (httpStatus >= 200 && httpStatus < 300) {
2773                 responseObject = this.createResponseObject(o, callback.argument);
2774                 if (callback.success) {
2775                     if (!callback.scope) {
2776                         callback.success(responseObject);
2777                     }
2778                     else {
2779
2780
2781                         callback.success.apply(callback.scope, [responseObject]);
2782                     }
2783                 }
2784             }
2785             else {
2786                 switch (httpStatus) {
2787
2788                     case 12002:
2789                     case 12029:
2790                     case 12030:
2791                     case 12031:
2792                     case 12152:
2793                     case 13030:
2794                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2795                         if (callback.failure) {
2796                             if (!callback.scope) {
2797                                 callback.failure(responseObject);
2798                             }
2799                             else {
2800                                 callback.failure.apply(callback.scope, [responseObject]);
2801                             }
2802                         }
2803                         break;
2804                     default:
2805                         responseObject = this.createResponseObject(o, callback.argument);
2806                         if (callback.failure) {
2807                             if (!callback.scope) {
2808                                 callback.failure(responseObject);
2809                             }
2810                             else {
2811                                 callback.failure.apply(callback.scope, [responseObject]);
2812                             }
2813                         }
2814                 }
2815             }
2816
2817             this.releaseObject(o);
2818             responseObject = null;
2819         },
2820
2821         createResponseObject:function(o, callbackArg)
2822         {
2823             var obj = {};
2824             var headerObj = {};
2825
2826             try
2827             {
2828                 var headerStr = o.conn.getAllResponseHeaders();
2829                 var header = headerStr.split('\n');
2830                 for (var i = 0; i < header.length; i++) {
2831                     var delimitPos = header[i].indexOf(':');
2832                     if (delimitPos != -1) {
2833                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2834                     }
2835                 }
2836             }
2837             catch(e) {
2838             }
2839
2840             obj.tId = o.tId;
2841             obj.status = o.conn.status;
2842             obj.statusText = o.conn.statusText;
2843             obj.getResponseHeader = headerObj;
2844             obj.getAllResponseHeaders = headerStr;
2845             obj.responseText = o.conn.responseText;
2846             obj.responseXML = o.conn.responseXML;
2847
2848             if (typeof callbackArg !== undefined) {
2849                 obj.argument = callbackArg;
2850             }
2851
2852             return obj;
2853         },
2854
2855         createExceptionObject:function(tId, callbackArg, isAbort)
2856         {
2857             var COMM_CODE = 0;
2858             var COMM_ERROR = 'communication failure';
2859             var ABORT_CODE = -1;
2860             var ABORT_ERROR = 'transaction aborted';
2861
2862             var obj = {};
2863
2864             obj.tId = tId;
2865             if (isAbort) {
2866                 obj.status = ABORT_CODE;
2867                 obj.statusText = ABORT_ERROR;
2868             }
2869             else {
2870                 obj.status = COMM_CODE;
2871                 obj.statusText = COMM_ERROR;
2872             }
2873
2874             if (callbackArg) {
2875                 obj.argument = callbackArg;
2876             }
2877
2878             return obj;
2879         },
2880
2881         initHeader:function(label, value, isDefault)
2882         {
2883             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2884
2885             if (headerObj[label] === undefined) {
2886                 headerObj[label] = value;
2887             }
2888             else {
2889
2890
2891                 headerObj[label] = value + "," + headerObj[label];
2892             }
2893
2894             if (isDefault) {
2895                 this.hasDefaultHeaders = true;
2896             }
2897             else {
2898                 this.hasHeaders = true;
2899             }
2900         },
2901
2902
2903         setHeader:function(o)
2904         {
2905             if (this.hasDefaultHeaders) {
2906                 for (var prop in this.defaultHeaders) {
2907                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2908                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2909                     }
2910                 }
2911             }
2912
2913             if (this.hasHeaders) {
2914                 for (var prop in this.headers) {
2915                     if (this.headers.hasOwnProperty(prop)) {
2916                         o.conn.setRequestHeader(prop, this.headers[prop]);
2917                     }
2918                 }
2919                 this.headers = {};
2920                 this.hasHeaders = false;
2921             }
2922         },
2923
2924         resetDefaultHeaders:function() {
2925             delete this.defaultHeaders;
2926             this.defaultHeaders = {};
2927             this.hasDefaultHeaders = false;
2928         },
2929
2930         abort:function(o, callback, isTimeout)
2931         {
2932             if(this.isCallInProgress(o)) {
2933                 o.conn.abort();
2934                 window.clearInterval(this.poll[o.tId]);
2935                 delete this.poll[o.tId];
2936                 if (isTimeout) {
2937                     delete this.timeout[o.tId];
2938                 }
2939
2940                 this.handleTransactionResponse(o, callback, true);
2941
2942                 return true;
2943             }
2944             else {
2945                 return false;
2946             }
2947         },
2948
2949
2950         isCallInProgress:function(o)
2951         {
2952             if (o && o.conn) {
2953                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2954             }
2955             else {
2956
2957                 return false;
2958             }
2959         },
2960
2961
2962         releaseObject:function(o)
2963         {
2964
2965             o.conn = null;
2966
2967             o = null;
2968         },
2969
2970         activeX:[
2971         'MSXML2.XMLHTTP.3.0',
2972         'MSXML2.XMLHTTP',
2973         'Microsoft.XMLHTTP'
2974         ]
2975
2976
2977     };
2978 })();/*
2979  * Portions of this file are based on pieces of Yahoo User Interface Library
2980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2981  * YUI licensed under the BSD License:
2982  * http://developer.yahoo.net/yui/license.txt
2983  * <script type="text/javascript">
2984  *
2985  */
2986
2987 Roo.lib.Region = function(t, r, b, l) {
2988     this.top = t;
2989     this[1] = t;
2990     this.right = r;
2991     this.bottom = b;
2992     this.left = l;
2993     this[0] = l;
2994 };
2995
2996
2997 Roo.lib.Region.prototype = {
2998     contains : function(region) {
2999         return ( region.left >= this.left &&
3000                  region.right <= this.right &&
3001                  region.top >= this.top &&
3002                  region.bottom <= this.bottom    );
3003
3004     },
3005
3006     getArea : function() {
3007         return ( (this.bottom - this.top) * (this.right - this.left) );
3008     },
3009
3010     intersect : function(region) {
3011         var t = Math.max(this.top, region.top);
3012         var r = Math.min(this.right, region.right);
3013         var b = Math.min(this.bottom, region.bottom);
3014         var l = Math.max(this.left, region.left);
3015
3016         if (b >= t && r >= l) {
3017             return new Roo.lib.Region(t, r, b, l);
3018         } else {
3019             return null;
3020         }
3021     },
3022     union : function(region) {
3023         var t = Math.min(this.top, region.top);
3024         var r = Math.max(this.right, region.right);
3025         var b = Math.max(this.bottom, region.bottom);
3026         var l = Math.min(this.left, region.left);
3027
3028         return new Roo.lib.Region(t, r, b, l);
3029     },
3030
3031     adjust : function(t, l, b, r) {
3032         this.top += t;
3033         this.left += l;
3034         this.right += r;
3035         this.bottom += b;
3036         return this;
3037     }
3038 };
3039
3040 Roo.lib.Region.getRegion = function(el) {
3041     var p = Roo.lib.Dom.getXY(el);
3042
3043     var t = p[1];
3044     var r = p[0] + el.offsetWidth;
3045     var b = p[1] + el.offsetHeight;
3046     var l = p[0];
3047
3048     return new Roo.lib.Region(t, r, b, l);
3049 };
3050 /*
3051  * Portions of this file are based on pieces of Yahoo User Interface Library
3052  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3053  * YUI licensed under the BSD License:
3054  * http://developer.yahoo.net/yui/license.txt
3055  * <script type="text/javascript">
3056  *
3057  */
3058 //@@dep Roo.lib.Region
3059
3060
3061 Roo.lib.Point = function(x, y) {
3062     if (x instanceof Array) {
3063         y = x[1];
3064         x = x[0];
3065     }
3066     this.x = this.right = this.left = this[0] = x;
3067     this.y = this.top = this.bottom = this[1] = y;
3068 };
3069
3070 Roo.lib.Point.prototype = new Roo.lib.Region();
3071 /*
3072  * Portions of this file are based on pieces of Yahoo User Interface Library
3073  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3074  * YUI licensed under the BSD License:
3075  * http://developer.yahoo.net/yui/license.txt
3076  * <script type="text/javascript">
3077  *
3078  */
3079  
3080 (function() {   
3081
3082     Roo.lib.Anim = {
3083         scroll : function(el, args, duration, easing, cb, scope) {
3084             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3085         },
3086
3087         motion : function(el, args, duration, easing, cb, scope) {
3088             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3089         },
3090
3091         color : function(el, args, duration, easing, cb, scope) {
3092             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3093         },
3094
3095         run : function(el, args, duration, easing, cb, scope, type) {
3096             type = type || Roo.lib.AnimBase;
3097             if (typeof easing == "string") {
3098                 easing = Roo.lib.Easing[easing];
3099             }
3100             var anim = new type(el, args, duration, easing);
3101             anim.animateX(function() {
3102                 Roo.callback(cb, scope);
3103             });
3104             return anim;
3105         }
3106     };
3107 })();/*
3108  * Portions of this file are based on pieces of Yahoo User Interface Library
3109  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3110  * YUI licensed under the BSD License:
3111  * http://developer.yahoo.net/yui/license.txt
3112  * <script type="text/javascript">
3113  *
3114  */
3115
3116 (function() {    
3117     var libFlyweight;
3118     
3119     function fly(el) {
3120         if (!libFlyweight) {
3121             libFlyweight = new Roo.Element.Flyweight();
3122         }
3123         libFlyweight.dom = el;
3124         return libFlyweight;
3125     }
3126
3127     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3128     
3129    
3130     
3131     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3132         if (el) {
3133             this.init(el, attributes, duration, method);
3134         }
3135     };
3136
3137     Roo.lib.AnimBase.fly = fly;
3138     
3139     
3140     
3141     Roo.lib.AnimBase.prototype = {
3142
3143         toString: function() {
3144             var el = this.getEl();
3145             var id = el.id || el.tagName;
3146             return ("Anim " + id);
3147         },
3148
3149         patterns: {
3150             noNegatives:        /width|height|opacity|padding/i,
3151             offsetAttribute:  /^((width|height)|(top|left))$/,
3152             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3153             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3154         },
3155
3156
3157         doMethod: function(attr, start, end) {
3158             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3159         },
3160
3161
3162         setAttribute: function(attr, val, unit) {
3163             if (this.patterns.noNegatives.test(attr)) {
3164                 val = (val > 0) ? val : 0;
3165             }
3166
3167             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3168         },
3169
3170
3171         getAttribute: function(attr) {
3172             var el = this.getEl();
3173             var val = fly(el).getStyle(attr);
3174
3175             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3176                 return parseFloat(val);
3177             }
3178
3179             var a = this.patterns.offsetAttribute.exec(attr) || [];
3180             var pos = !!( a[3] );
3181             var box = !!( a[2] );
3182
3183
3184             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3185                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3186             } else {
3187                 val = 0;
3188             }
3189
3190             return val;
3191         },
3192
3193
3194         getDefaultUnit: function(attr) {
3195             if (this.patterns.defaultUnit.test(attr)) {
3196                 return 'px';
3197             }
3198
3199             return '';
3200         },
3201
3202         animateX : function(callback, scope) {
3203             var f = function() {
3204                 this.onComplete.removeListener(f);
3205                 if (typeof callback == "function") {
3206                     callback.call(scope || this, this);
3207                 }
3208             };
3209             this.onComplete.addListener(f, this);
3210             this.animate();
3211         },
3212
3213
3214         setRuntimeAttribute: function(attr) {
3215             var start;
3216             var end;
3217             var attributes = this.attributes;
3218
3219             this.runtimeAttributes[attr] = {};
3220
3221             var isset = function(prop) {
3222                 return (typeof prop !== 'undefined');
3223             };
3224
3225             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3226                 return false;
3227             }
3228
3229             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3230
3231
3232             if (isset(attributes[attr]['to'])) {
3233                 end = attributes[attr]['to'];
3234             } else if (isset(attributes[attr]['by'])) {
3235                 if (start.constructor == Array) {
3236                     end = [];
3237                     for (var i = 0, len = start.length; i < len; ++i) {
3238                         end[i] = start[i] + attributes[attr]['by'][i];
3239                     }
3240                 } else {
3241                     end = start + attributes[attr]['by'];
3242                 }
3243             }
3244
3245             this.runtimeAttributes[attr].start = start;
3246             this.runtimeAttributes[attr].end = end;
3247
3248
3249             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3250         },
3251
3252
3253         init: function(el, attributes, duration, method) {
3254
3255             var isAnimated = false;
3256
3257
3258             var startTime = null;
3259
3260
3261             var actualFrames = 0;
3262
3263
3264             el = Roo.getDom(el);
3265
3266
3267             this.attributes = attributes || {};
3268
3269
3270             this.duration = duration || 1;
3271
3272
3273             this.method = method || Roo.lib.Easing.easeNone;
3274
3275
3276             this.useSeconds = true;
3277
3278
3279             this.currentFrame = 0;
3280
3281
3282             this.totalFrames = Roo.lib.AnimMgr.fps;
3283
3284
3285             this.getEl = function() {
3286                 return el;
3287             };
3288
3289
3290             this.isAnimated = function() {
3291                 return isAnimated;
3292             };
3293
3294
3295             this.getStartTime = function() {
3296                 return startTime;
3297             };
3298
3299             this.runtimeAttributes = {};
3300
3301
3302             this.animate = function() {
3303                 if (this.isAnimated()) {
3304                     return false;
3305                 }
3306
3307                 this.currentFrame = 0;
3308
3309                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3310
3311                 Roo.lib.AnimMgr.registerElement(this);
3312             };
3313
3314
3315             this.stop = function(finish) {
3316                 if (finish) {
3317                     this.currentFrame = this.totalFrames;
3318                     this._onTween.fire();
3319                 }
3320                 Roo.lib.AnimMgr.stop(this);
3321             };
3322
3323             var onStart = function() {
3324                 this.onStart.fire();
3325
3326                 this.runtimeAttributes = {};
3327                 for (var attr in this.attributes) {
3328                     this.setRuntimeAttribute(attr);
3329                 }
3330
3331                 isAnimated = true;
3332                 actualFrames = 0;
3333                 startTime = new Date();
3334             };
3335
3336
3337             var onTween = function() {
3338                 var data = {
3339                     duration: new Date() - this.getStartTime(),
3340                     currentFrame: this.currentFrame
3341                 };
3342
3343                 data.toString = function() {
3344                     return (
3345                             'duration: ' + data.duration +
3346                             ', currentFrame: ' + data.currentFrame
3347                             );
3348                 };
3349
3350                 this.onTween.fire(data);
3351
3352                 var runtimeAttributes = this.runtimeAttributes;
3353
3354                 for (var attr in runtimeAttributes) {
3355                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3356                 }
3357
3358                 actualFrames += 1;
3359             };
3360
3361             var onComplete = function() {
3362                 var actual_duration = (new Date() - startTime) / 1000 ;
3363
3364                 var data = {
3365                     duration: actual_duration,
3366                     frames: actualFrames,
3367                     fps: actualFrames / actual_duration
3368                 };
3369
3370                 data.toString = function() {
3371                     return (
3372                             'duration: ' + data.duration +
3373                             ', frames: ' + data.frames +
3374                             ', fps: ' + data.fps
3375                             );
3376                 };
3377
3378                 isAnimated = false;
3379                 actualFrames = 0;
3380                 this.onComplete.fire(data);
3381             };
3382
3383
3384             this._onStart = new Roo.util.Event(this);
3385             this.onStart = new Roo.util.Event(this);
3386             this.onTween = new Roo.util.Event(this);
3387             this._onTween = new Roo.util.Event(this);
3388             this.onComplete = new Roo.util.Event(this);
3389             this._onComplete = new Roo.util.Event(this);
3390             this._onStart.addListener(onStart);
3391             this._onTween.addListener(onTween);
3392             this._onComplete.addListener(onComplete);
3393         }
3394     };
3395 })();
3396 /*
3397  * Portions of this file are based on pieces of Yahoo User Interface Library
3398  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3399  * YUI licensed under the BSD License:
3400  * http://developer.yahoo.net/yui/license.txt
3401  * <script type="text/javascript">
3402  *
3403  */
3404
3405 Roo.lib.AnimMgr = new function() {
3406
3407     var thread = null;
3408
3409
3410     var queue = [];
3411
3412
3413     var tweenCount = 0;
3414
3415
3416     this.fps = 1000;
3417
3418
3419     this.delay = 1;
3420
3421
3422     this.registerElement = function(tween) {
3423         queue[queue.length] = tween;
3424         tweenCount += 1;
3425         tween._onStart.fire();
3426         this.start();
3427     };
3428
3429
3430     this.unRegister = function(tween, index) {
3431         tween._onComplete.fire();
3432         index = index || getIndex(tween);
3433         if (index != -1) {
3434             queue.splice(index, 1);
3435         }
3436
3437         tweenCount -= 1;
3438         if (tweenCount <= 0) {
3439             this.stop();
3440         }
3441     };
3442
3443
3444     this.start = function() {
3445         if (thread === null) {
3446             thread = setInterval(this.run, this.delay);
3447         }
3448     };
3449
3450
3451     this.stop = function(tween) {
3452         if (!tween) {
3453             clearInterval(thread);
3454
3455             for (var i = 0, len = queue.length; i < len; ++i) {
3456                 if (queue[0].isAnimated()) {
3457                     this.unRegister(queue[0], 0);
3458                 }
3459             }
3460
3461             queue = [];
3462             thread = null;
3463             tweenCount = 0;
3464         }
3465         else {
3466             this.unRegister(tween);
3467         }
3468     };
3469
3470
3471     this.run = function() {
3472         for (var i = 0, len = queue.length; i < len; ++i) {
3473             var tween = queue[i];
3474             if (!tween || !tween.isAnimated()) {
3475                 continue;
3476             }
3477
3478             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3479             {
3480                 tween.currentFrame += 1;
3481
3482                 if (tween.useSeconds) {
3483                     correctFrame(tween);
3484                 }
3485                 tween._onTween.fire();
3486             }
3487             else {
3488                 Roo.lib.AnimMgr.stop(tween, i);
3489             }
3490         }
3491     };
3492
3493     var getIndex = function(anim) {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             if (queue[i] == anim) {
3496                 return i;
3497             }
3498         }
3499         return -1;
3500     };
3501
3502
3503     var correctFrame = function(tween) {
3504         var frames = tween.totalFrames;
3505         var frame = tween.currentFrame;
3506         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3507         var elapsed = (new Date() - tween.getStartTime());
3508         var tweak = 0;
3509
3510         if (elapsed < tween.duration * 1000) {
3511             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3512         } else {
3513             tweak = frames - (frame + 1);
3514         }
3515         if (tweak > 0 && isFinite(tweak)) {
3516             if (tween.currentFrame + tweak >= frames) {
3517                 tweak = frames - (frame + 1);
3518             }
3519
3520             tween.currentFrame += tweak;
3521         }
3522     };
3523 };
3524
3525     /*
3526  * Portions of this file are based on pieces of Yahoo User Interface Library
3527  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3528  * YUI licensed under the BSD License:
3529  * http://developer.yahoo.net/yui/license.txt
3530  * <script type="text/javascript">
3531  *
3532  */
3533 Roo.lib.Bezier = new function() {
3534
3535         this.getPosition = function(points, t) {
3536             var n = points.length;
3537             var tmp = [];
3538
3539             for (var i = 0; i < n; ++i) {
3540                 tmp[i] = [points[i][0], points[i][1]];
3541             }
3542
3543             for (var j = 1; j < n; ++j) {
3544                 for (i = 0; i < n - j; ++i) {
3545                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3546                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3547                 }
3548             }
3549
3550             return [ tmp[0][0], tmp[0][1] ];
3551
3552         };
3553     };/*
3554  * Portions of this file are based on pieces of Yahoo User Interface Library
3555  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3556  * YUI licensed under the BSD License:
3557  * http://developer.yahoo.net/yui/license.txt
3558  * <script type="text/javascript">
3559  *
3560  */
3561 (function() {
3562
3563     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3564         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3565     };
3566
3567     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3568
3569     var fly = Roo.lib.AnimBase.fly;
3570     var Y = Roo.lib;
3571     var superclass = Y.ColorAnim.superclass;
3572     var proto = Y.ColorAnim.prototype;
3573
3574     proto.toString = function() {
3575         var el = this.getEl();
3576         var id = el.id || el.tagName;
3577         return ("ColorAnim " + id);
3578     };
3579
3580     proto.patterns.color = /color$/i;
3581     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3582     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3583     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3584     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3585
3586
3587     proto.parseColor = function(s) {
3588         if (s.length == 3) {
3589             return s;
3590         }
3591
3592         var c = this.patterns.hex.exec(s);
3593         if (c && c.length == 4) {
3594             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3595         }
3596
3597         c = this.patterns.rgb.exec(s);
3598         if (c && c.length == 4) {
3599             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3600         }
3601
3602         c = this.patterns.hex3.exec(s);
3603         if (c && c.length == 4) {
3604             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3605         }
3606
3607         return null;
3608     };
3609     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3610     proto.getAttribute = function(attr) {
3611         var el = this.getEl();
3612         if (this.patterns.color.test(attr)) {
3613             var val = fly(el).getStyle(attr);
3614
3615             if (this.patterns.transparent.test(val)) {
3616                 var parent = el.parentNode;
3617                 val = fly(parent).getStyle(attr);
3618
3619                 while (parent && this.patterns.transparent.test(val)) {
3620                     parent = parent.parentNode;
3621                     val = fly(parent).getStyle(attr);
3622                     if (parent.tagName.toUpperCase() == 'HTML') {
3623                         val = '#fff';
3624                     }
3625                 }
3626             }
3627         } else {
3628             val = superclass.getAttribute.call(this, attr);
3629         }
3630
3631         return val;
3632     };
3633     proto.getAttribute = function(attr) {
3634         var el = this.getEl();
3635         if (this.patterns.color.test(attr)) {
3636             var val = fly(el).getStyle(attr);
3637
3638             if (this.patterns.transparent.test(val)) {
3639                 var parent = el.parentNode;
3640                 val = fly(parent).getStyle(attr);
3641
3642                 while (parent && this.patterns.transparent.test(val)) {
3643                     parent = parent.parentNode;
3644                     val = fly(parent).getStyle(attr);
3645                     if (parent.tagName.toUpperCase() == 'HTML') {
3646                         val = '#fff';
3647                     }
3648                 }
3649             }
3650         } else {
3651             val = superclass.getAttribute.call(this, attr);
3652         }
3653
3654         return val;
3655     };
3656
3657     proto.doMethod = function(attr, start, end) {
3658         var val;
3659
3660         if (this.patterns.color.test(attr)) {
3661             val = [];
3662             for (var i = 0, len = start.length; i < len; ++i) {
3663                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3664             }
3665
3666             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3667         }
3668         else {
3669             val = superclass.doMethod.call(this, attr, start, end);
3670         }
3671
3672         return val;
3673     };
3674
3675     proto.setRuntimeAttribute = function(attr) {
3676         superclass.setRuntimeAttribute.call(this, attr);
3677
3678         if (this.patterns.color.test(attr)) {
3679             var attributes = this.attributes;
3680             var start = this.parseColor(this.runtimeAttributes[attr].start);
3681             var end = this.parseColor(this.runtimeAttributes[attr].end);
3682
3683             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3684                 end = this.parseColor(attributes[attr].by);
3685
3686                 for (var i = 0, len = start.length; i < len; ++i) {
3687                     end[i] = start[i] + end[i];
3688                 }
3689             }
3690
3691             this.runtimeAttributes[attr].start = start;
3692             this.runtimeAttributes[attr].end = end;
3693         }
3694     };
3695 })();
3696
3697 /*
3698  * Portions of this file are based on pieces of Yahoo User Interface Library
3699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3700  * YUI licensed under the BSD License:
3701  * http://developer.yahoo.net/yui/license.txt
3702  * <script type="text/javascript">
3703  *
3704  */
3705 Roo.lib.Easing = {
3706
3707
3708     easeNone: function (t, b, c, d) {
3709         return c * t / d + b;
3710     },
3711
3712
3713     easeIn: function (t, b, c, d) {
3714         return c * (t /= d) * t + b;
3715     },
3716
3717
3718     easeOut: function (t, b, c, d) {
3719         return -c * (t /= d) * (t - 2) + b;
3720     },
3721
3722
3723     easeBoth: function (t, b, c, d) {
3724         if ((t /= d / 2) < 1) {
3725             return c / 2 * t * t + b;
3726         }
3727
3728         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3729     },
3730
3731
3732     easeInStrong: function (t, b, c, d) {
3733         return c * (t /= d) * t * t * t + b;
3734     },
3735
3736
3737     easeOutStrong: function (t, b, c, d) {
3738         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3739     },
3740
3741
3742     easeBothStrong: function (t, b, c, d) {
3743         if ((t /= d / 2) < 1) {
3744             return c / 2 * t * t * t * t + b;
3745         }
3746
3747         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3748     },
3749
3750
3751
3752     elasticIn: function (t, b, c, d, a, p) {
3753         if (t == 0) {
3754             return b;
3755         }
3756         if ((t /= d) == 1) {
3757             return b + c;
3758         }
3759         if (!p) {
3760             p = d * .3;
3761         }
3762
3763         if (!a || a < Math.abs(c)) {
3764             a = c;
3765             var s = p / 4;
3766         }
3767         else {
3768             var s = p / (2 * Math.PI) * Math.asin(c / a);
3769         }
3770
3771         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3772     },
3773
3774
3775     elasticOut: function (t, b, c, d, a, p) {
3776         if (t == 0) {
3777             return b;
3778         }
3779         if ((t /= d) == 1) {
3780             return b + c;
3781         }
3782         if (!p) {
3783             p = d * .3;
3784         }
3785
3786         if (!a || a < Math.abs(c)) {
3787             a = c;
3788             var s = p / 4;
3789         }
3790         else {
3791             var s = p / (2 * Math.PI) * Math.asin(c / a);
3792         }
3793
3794         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3795     },
3796
3797
3798     elasticBoth: function (t, b, c, d, a, p) {
3799         if (t == 0) {
3800             return b;
3801         }
3802
3803         if ((t /= d / 2) == 2) {
3804             return b + c;
3805         }
3806
3807         if (!p) {
3808             p = d * (.3 * 1.5);
3809         }
3810
3811         if (!a || a < Math.abs(c)) {
3812             a = c;
3813             var s = p / 4;
3814         }
3815         else {
3816             var s = p / (2 * Math.PI) * Math.asin(c / a);
3817         }
3818
3819         if (t < 1) {
3820             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3821                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3822         }
3823         return a * Math.pow(2, -10 * (t -= 1)) *
3824                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3825     },
3826
3827
3828
3829     backIn: function (t, b, c, d, s) {
3830         if (typeof s == 'undefined') {
3831             s = 1.70158;
3832         }
3833         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3834     },
3835
3836
3837     backOut: function (t, b, c, d, s) {
3838         if (typeof s == 'undefined') {
3839             s = 1.70158;
3840         }
3841         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3842     },
3843
3844
3845     backBoth: function (t, b, c, d, s) {
3846         if (typeof s == 'undefined') {
3847             s = 1.70158;
3848         }
3849
3850         if ((t /= d / 2 ) < 1) {
3851             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3852         }
3853         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3854     },
3855
3856
3857     bounceIn: function (t, b, c, d) {
3858         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3859     },
3860
3861
3862     bounceOut: function (t, b, c, d) {
3863         if ((t /= d) < (1 / 2.75)) {
3864             return c * (7.5625 * t * t) + b;
3865         } else if (t < (2 / 2.75)) {
3866             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3867         } else if (t < (2.5 / 2.75)) {
3868             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3869         }
3870         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3871     },
3872
3873
3874     bounceBoth: function (t, b, c, d) {
3875         if (t < d / 2) {
3876             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3877         }
3878         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3879     }
3880 };/*
3881  * Portions of this file are based on pieces of Yahoo User Interface Library
3882  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3883  * YUI licensed under the BSD License:
3884  * http://developer.yahoo.net/yui/license.txt
3885  * <script type="text/javascript">
3886  *
3887  */
3888     (function() {
3889         Roo.lib.Motion = function(el, attributes, duration, method) {
3890             if (el) {
3891                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3892             }
3893         };
3894
3895         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3896
3897
3898         var Y = Roo.lib;
3899         var superclass = Y.Motion.superclass;
3900         var proto = Y.Motion.prototype;
3901
3902         proto.toString = function() {
3903             var el = this.getEl();
3904             var id = el.id || el.tagName;
3905             return ("Motion " + id);
3906         };
3907
3908         proto.patterns.points = /^points$/i;
3909
3910         proto.setAttribute = function(attr, val, unit) {
3911             if (this.patterns.points.test(attr)) {
3912                 unit = unit || 'px';
3913                 superclass.setAttribute.call(this, 'left', val[0], unit);
3914                 superclass.setAttribute.call(this, 'top', val[1], unit);
3915             } else {
3916                 superclass.setAttribute.call(this, attr, val, unit);
3917             }
3918         };
3919
3920         proto.getAttribute = function(attr) {
3921             if (this.patterns.points.test(attr)) {
3922                 var val = [
3923                         superclass.getAttribute.call(this, 'left'),
3924                         superclass.getAttribute.call(this, 'top')
3925                         ];
3926             } else {
3927                 val = superclass.getAttribute.call(this, attr);
3928             }
3929
3930             return val;
3931         };
3932
3933         proto.doMethod = function(attr, start, end) {
3934             var val = null;
3935
3936             if (this.patterns.points.test(attr)) {
3937                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3938                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3939             } else {
3940                 val = superclass.doMethod.call(this, attr, start, end);
3941             }
3942             return val;
3943         };
3944
3945         proto.setRuntimeAttribute = function(attr) {
3946             if (this.patterns.points.test(attr)) {
3947                 var el = this.getEl();
3948                 var attributes = this.attributes;
3949                 var start;
3950                 var control = attributes['points']['control'] || [];
3951                 var end;
3952                 var i, len;
3953
3954                 if (control.length > 0 && !(control[0] instanceof Array)) {
3955                     control = [control];
3956                 } else {
3957                     var tmp = [];
3958                     for (i = 0,len = control.length; i < len; ++i) {
3959                         tmp[i] = control[i];
3960                     }
3961                     control = tmp;
3962                 }
3963
3964                 Roo.fly(el).position();
3965
3966                 if (isset(attributes['points']['from'])) {
3967                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3968                 }
3969                 else {
3970                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3971                 }
3972
3973                 start = this.getAttribute('points');
3974
3975
3976                 if (isset(attributes['points']['to'])) {
3977                     end = translateValues.call(this, attributes['points']['to'], start);
3978
3979                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         control[i] = translateValues.call(this, control[i], start);
3982                     }
3983
3984
3985                 } else if (isset(attributes['points']['by'])) {
3986                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3987
3988                     for (i = 0,len = control.length; i < len; ++i) {
3989                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3990                     }
3991                 }
3992
3993                 this.runtimeAttributes[attr] = [start];
3994
3995                 if (control.length > 0) {
3996                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3997                 }
3998
3999                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4000             }
4001             else {
4002                 superclass.setRuntimeAttribute.call(this, attr);
4003             }
4004         };
4005
4006         var translateValues = function(val, start) {
4007             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4008             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4009
4010             return val;
4011         };
4012
4013         var isset = function(prop) {
4014             return (typeof prop !== 'undefined');
4015         };
4016     })();
4017 /*
4018  * Portions of this file are based on pieces of Yahoo User Interface Library
4019  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4020  * YUI licensed under the BSD License:
4021  * http://developer.yahoo.net/yui/license.txt
4022  * <script type="text/javascript">
4023  *
4024  */
4025     (function() {
4026         Roo.lib.Scroll = function(el, attributes, duration, method) {
4027             if (el) {
4028                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4029             }
4030         };
4031
4032         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4033
4034
4035         var Y = Roo.lib;
4036         var superclass = Y.Scroll.superclass;
4037         var proto = Y.Scroll.prototype;
4038
4039         proto.toString = function() {
4040             var el = this.getEl();
4041             var id = el.id || el.tagName;
4042             return ("Scroll " + id);
4043         };
4044
4045         proto.doMethod = function(attr, start, end) {
4046             var val = null;
4047
4048             if (attr == 'scroll') {
4049                 val = [
4050                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4051                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4052                         ];
4053
4054             } else {
4055                 val = superclass.doMethod.call(this, attr, start, end);
4056             }
4057             return val;
4058         };
4059
4060         proto.getAttribute = function(attr) {
4061             var val = null;
4062             var el = this.getEl();
4063
4064             if (attr == 'scroll') {
4065                 val = [ el.scrollLeft, el.scrollTop ];
4066             } else {
4067                 val = superclass.getAttribute.call(this, attr);
4068             }
4069
4070             return val;
4071         };
4072
4073         proto.setAttribute = function(attr, val, unit) {
4074             var el = this.getEl();
4075
4076             if (attr == 'scroll') {
4077                 el.scrollLeft = val[0];
4078                 el.scrollTop = val[1];
4079             } else {
4080                 superclass.setAttribute.call(this, attr, val, unit);
4081             }
4082         };
4083     })();
4084 /*
4085  * Based on:
4086  * Ext JS Library 1.1.1
4087  * Copyright(c) 2006-2007, Ext JS, LLC.
4088  *
4089  * Originally Released Under LGPL - original licence link has changed is not relivant.
4090  *
4091  * Fork - LGPL
4092  * <script type="text/javascript">
4093  */
4094
4095
4096 // nasty IE9 hack - what a pile of crap that is..
4097
4098  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4099     Range.prototype.createContextualFragment = function (html) {
4100         var doc = window.document;
4101         var container = doc.createElement("div");
4102         container.innerHTML = html;
4103         var frag = doc.createDocumentFragment(), n;
4104         while ((n = container.firstChild)) {
4105             frag.appendChild(n);
4106         }
4107         return frag;
4108     };
4109 }
4110
4111 /**
4112  * @class Roo.DomHelper
4113  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4114  * 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>.
4115  * @singleton
4116  */
4117 Roo.DomHelper = function(){
4118     var tempTableEl = null;
4119     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4120     var tableRe = /^table|tbody|tr|td$/i;
4121     var xmlns = {};
4122     // build as innerHTML where available
4123     /** @ignore */
4124     var createHtml = function(o){
4125         if(typeof o == 'string'){
4126             return o;
4127         }
4128         var b = "";
4129         if(!o.tag){
4130             o.tag = "div";
4131         }
4132         b += "<" + o.tag;
4133         for(var attr in o){
4134             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4135             if(attr == "style"){
4136                 var s = o["style"];
4137                 if(typeof s == "function"){
4138                     s = s.call();
4139                 }
4140                 if(typeof s == "string"){
4141                     b += ' style="' + s + '"';
4142                 }else if(typeof s == "object"){
4143                     b += ' style="';
4144                     for(var key in s){
4145                         if(typeof s[key] != "function"){
4146                             b += key + ":" + s[key] + ";";
4147                         }
4148                     }
4149                     b += '"';
4150                 }
4151             }else{
4152                 if(attr == "cls"){
4153                     b += ' class="' + o["cls"] + '"';
4154                 }else if(attr == "htmlFor"){
4155                     b += ' for="' + o["htmlFor"] + '"';
4156                 }else{
4157                     b += " " + attr + '="' + o[attr] + '"';
4158                 }
4159             }
4160         }
4161         if(emptyTags.test(o.tag)){
4162             b += "/>";
4163         }else{
4164             b += ">";
4165             var cn = o.children || o.cn;
4166             if(cn){
4167                 //http://bugs.kde.org/show_bug.cgi?id=71506
4168                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4169                     for(var i = 0, len = cn.length; i < len; i++) {
4170                         b += createHtml(cn[i], b);
4171                     }
4172                 }else{
4173                     b += createHtml(cn, b);
4174                 }
4175             }
4176             if(o.html){
4177                 b += o.html;
4178             }
4179             b += "</" + o.tag + ">";
4180         }
4181         return b;
4182     };
4183
4184     // build as dom
4185     /** @ignore */
4186     var createDom = function(o, parentNode){
4187          
4188         // defininition craeted..
4189         var ns = false;
4190         if (o.ns && o.ns != 'html') {
4191                
4192             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4193                 xmlns[o.ns] = o.xmlns;
4194                 ns = o.xmlns;
4195             }
4196             if (typeof(xmlns[o.ns]) == 'undefined') {
4197                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4198             }
4199             ns = xmlns[o.ns];
4200         }
4201         
4202         
4203         if (typeof(o) == 'string') {
4204             return parentNode.appendChild(document.createTextNode(o));
4205         }
4206         o.tag = o.tag || div;
4207         if (o.ns && Roo.isIE) {
4208             ns = false;
4209             o.tag = o.ns + ':' + o.tag;
4210             
4211         }
4212         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4213         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4214         for(var attr in o){
4215             
4216             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4217                     attr == "style" || typeof o[attr] == "function") continue;
4218                     
4219             if(attr=="cls" && Roo.isIE){
4220                 el.className = o["cls"];
4221             }else{
4222                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4223                 else el[attr] = o[attr];
4224             }
4225         }
4226         Roo.DomHelper.applyStyles(el, o.style);
4227         var cn = o.children || o.cn;
4228         if(cn){
4229             //http://bugs.kde.org/show_bug.cgi?id=71506
4230              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4231                 for(var i = 0, len = cn.length; i < len; i++) {
4232                     createDom(cn[i], el);
4233                 }
4234             }else{
4235                 createDom(cn, el);
4236             }
4237         }
4238         if(o.html){
4239             el.innerHTML = o.html;
4240         }
4241         if(parentNode){
4242            parentNode.appendChild(el);
4243         }
4244         return el;
4245     };
4246
4247     var ieTable = function(depth, s, h, e){
4248         tempTableEl.innerHTML = [s, h, e].join('');
4249         var i = -1, el = tempTableEl;
4250         while(++i < depth){
4251             el = el.firstChild;
4252         }
4253         return el;
4254     };
4255
4256     // kill repeat to save bytes
4257     var ts = '<table>',
4258         te = '</table>',
4259         tbs = ts+'<tbody>',
4260         tbe = '</tbody>'+te,
4261         trs = tbs + '<tr>',
4262         tre = '</tr>'+tbe;
4263
4264     /**
4265      * @ignore
4266      * Nasty code for IE's broken table implementation
4267      */
4268     var insertIntoTable = function(tag, where, el, html){
4269         if(!tempTableEl){
4270             tempTableEl = document.createElement('div');
4271         }
4272         var node;
4273         var before = null;
4274         if(tag == 'td'){
4275             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4276                 return;
4277             }
4278             if(where == 'beforebegin'){
4279                 before = el;
4280                 el = el.parentNode;
4281             } else{
4282                 before = el.nextSibling;
4283                 el = el.parentNode;
4284             }
4285             node = ieTable(4, trs, html, tre);
4286         }
4287         else if(tag == 'tr'){
4288             if(where == 'beforebegin'){
4289                 before = el;
4290                 el = el.parentNode;
4291                 node = ieTable(3, tbs, html, tbe);
4292             } else if(where == 'afterend'){
4293                 before = el.nextSibling;
4294                 el = el.parentNode;
4295                 node = ieTable(3, tbs, html, tbe);
4296             } else{ // INTO a TR
4297                 if(where == 'afterbegin'){
4298                     before = el.firstChild;
4299                 }
4300                 node = ieTable(4, trs, html, tre);
4301             }
4302         } else if(tag == 'tbody'){
4303             if(where == 'beforebegin'){
4304                 before = el;
4305                 el = el.parentNode;
4306                 node = ieTable(2, ts, html, te);
4307             } else if(where == 'afterend'){
4308                 before = el.nextSibling;
4309                 el = el.parentNode;
4310                 node = ieTable(2, ts, html, te);
4311             } else{
4312                 if(where == 'afterbegin'){
4313                     before = el.firstChild;
4314                 }
4315                 node = ieTable(3, tbs, html, tbe);
4316             }
4317         } else{ // TABLE
4318             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4319                 return;
4320             }
4321             if(where == 'afterbegin'){
4322                 before = el.firstChild;
4323             }
4324             node = ieTable(2, ts, html, te);
4325         }
4326         el.insertBefore(node, before);
4327         return node;
4328     };
4329
4330     return {
4331     /** True to force the use of DOM instead of html fragments @type Boolean */
4332     useDom : false,
4333
4334     /**
4335      * Returns the markup for the passed Element(s) config
4336      * @param {Object} o The Dom object spec (and children)
4337      * @return {String}
4338      */
4339     markup : function(o){
4340         return createHtml(o);
4341     },
4342
4343     /**
4344      * Applies a style specification to an element
4345      * @param {String/HTMLElement} el The element to apply styles to
4346      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4347      * a function which returns such a specification.
4348      */
4349     applyStyles : function(el, styles){
4350         if(styles){
4351            el = Roo.fly(el);
4352            if(typeof styles == "string"){
4353                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4354                var matches;
4355                while ((matches = re.exec(styles)) != null){
4356                    el.setStyle(matches[1], matches[2]);
4357                }
4358            }else if (typeof styles == "object"){
4359                for (var style in styles){
4360                   el.setStyle(style, styles[style]);
4361                }
4362            }else if (typeof styles == "function"){
4363                 Roo.DomHelper.applyStyles(el, styles.call());
4364            }
4365         }
4366     },
4367
4368     /**
4369      * Inserts an HTML fragment into the Dom
4370      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4371      * @param {HTMLElement} el The context element
4372      * @param {String} html The HTML fragmenet
4373      * @return {HTMLElement} The new node
4374      */
4375     insertHtml : function(where, el, html){
4376         where = where.toLowerCase();
4377         if(el.insertAdjacentHTML){
4378             if(tableRe.test(el.tagName)){
4379                 var rs;
4380                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4381                     return rs;
4382                 }
4383             }
4384             switch(where){
4385                 case "beforebegin":
4386                     el.insertAdjacentHTML('BeforeBegin', html);
4387                     return el.previousSibling;
4388                 case "afterbegin":
4389                     el.insertAdjacentHTML('AfterBegin', html);
4390                     return el.firstChild;
4391                 case "beforeend":
4392                     el.insertAdjacentHTML('BeforeEnd', html);
4393                     return el.lastChild;
4394                 case "afterend":
4395                     el.insertAdjacentHTML('AfterEnd', html);
4396                     return el.nextSibling;
4397             }
4398             throw 'Illegal insertion point -> "' + where + '"';
4399         }
4400         var range = el.ownerDocument.createRange();
4401         var frag;
4402         switch(where){
4403              case "beforebegin":
4404                 range.setStartBefore(el);
4405                 frag = range.createContextualFragment(html);
4406                 el.parentNode.insertBefore(frag, el);
4407                 return el.previousSibling;
4408              case "afterbegin":
4409                 if(el.firstChild){
4410                     range.setStartBefore(el.firstChild);
4411                     frag = range.createContextualFragment(html);
4412                     el.insertBefore(frag, el.firstChild);
4413                     return el.firstChild;
4414                 }else{
4415                     el.innerHTML = html;
4416                     return el.firstChild;
4417                 }
4418             case "beforeend":
4419                 if(el.lastChild){
4420                     range.setStartAfter(el.lastChild);
4421                     frag = range.createContextualFragment(html);
4422                     el.appendChild(frag);
4423                     return el.lastChild;
4424                 }else{
4425                     el.innerHTML = html;
4426                     return el.lastChild;
4427                 }
4428             case "afterend":
4429                 range.setStartAfter(el);
4430                 frag = range.createContextualFragment(html);
4431                 el.parentNode.insertBefore(frag, el.nextSibling);
4432                 return el.nextSibling;
4433             }
4434             throw 'Illegal insertion point -> "' + where + '"';
4435     },
4436
4437     /**
4438      * Creates new Dom element(s) and inserts them before el
4439      * @param {String/HTMLElement/Element} el The context element
4440      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4441      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4442      * @return {HTMLElement/Roo.Element} The new node
4443      */
4444     insertBefore : function(el, o, returnElement){
4445         return this.doInsert(el, o, returnElement, "beforeBegin");
4446     },
4447
4448     /**
4449      * Creates new Dom element(s) and inserts them after el
4450      * @param {String/HTMLElement/Element} el The context element
4451      * @param {Object} o The Dom object spec (and children)
4452      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4453      * @return {HTMLElement/Roo.Element} The new node
4454      */
4455     insertAfter : function(el, o, returnElement){
4456         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them as the first child of el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertFirst : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "afterBegin");
4468     },
4469
4470     // private
4471     doInsert : function(el, o, returnElement, pos, sibling){
4472         el = Roo.getDom(el);
4473         var newNode;
4474         if(this.useDom || o.ns){
4475             newNode = createDom(o, null);
4476             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4477         }else{
4478             var html = createHtml(o);
4479             newNode = this.insertHtml(pos, el, html);
4480         }
4481         return returnElement ? Roo.get(newNode, true) : newNode;
4482     },
4483
4484     /**
4485      * Creates new Dom element(s) and appends them to el
4486      * @param {String/HTMLElement/Element} el The context element
4487      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4488      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4489      * @return {HTMLElement/Roo.Element} The new node
4490      */
4491     append : function(el, o, returnElement){
4492         el = Roo.getDom(el);
4493         var newNode;
4494         if(this.useDom || o.ns){
4495             newNode = createDom(o, null);
4496             el.appendChild(newNode);
4497         }else{
4498             var html = createHtml(o);
4499             newNode = this.insertHtml("beforeEnd", el, html);
4500         }
4501         return returnElement ? Roo.get(newNode, true) : newNode;
4502     },
4503
4504     /**
4505      * Creates new Dom element(s) and overwrites the contents of el with them
4506      * @param {String/HTMLElement/Element} el The context element
4507      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4508      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4509      * @return {HTMLElement/Roo.Element} The new node
4510      */
4511     overwrite : function(el, o, returnElement){
4512         el = Roo.getDom(el);
4513         if (o.ns) {
4514           
4515             while (el.childNodes.length) {
4516                 el.removeChild(el.firstChild);
4517             }
4518             createDom(o, el);
4519         } else {
4520             el.innerHTML = createHtml(o);   
4521         }
4522         
4523         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4524     },
4525
4526     /**
4527      * Creates a new Roo.DomHelper.Template from the Dom object spec
4528      * @param {Object} o The Dom object spec (and children)
4529      * @return {Roo.DomHelper.Template} The new template
4530      */
4531     createTemplate : function(o){
4532         var html = createHtml(o);
4533         return new Roo.Template(html);
4534     }
4535     };
4536 }();
4537 /*
4538  * Based on:
4539  * Ext JS Library 1.1.1
4540  * Copyright(c) 2006-2007, Ext JS, LLC.
4541  *
4542  * Originally Released Under LGPL - original licence link has changed is not relivant.
4543  *
4544  * Fork - LGPL
4545  * <script type="text/javascript">
4546  */
4547  
4548 /**
4549 * @class Roo.Template
4550 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4551 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4552 * Usage:
4553 <pre><code>
4554 var t = new Roo.Template({
4555     html :  '&lt;div name="{id}"&gt;' + 
4556         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4557         '&lt;/div&gt;',
4558     myformat: function (value, allValues) {
4559         return 'XX' + value;
4560     }
4561 });
4562 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4563 </code></pre>
4564 * For more information see this blog post with examples:
4565 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4566      - Create Elements using DOM, HTML fragments and Templates</a>. 
4567 * @constructor
4568 * @param {Object} cfg - Configuration object.
4569 */
4570 Roo.Template = function(cfg){
4571     // BC!
4572     if(cfg instanceof Array){
4573         cfg = cfg.join("");
4574     }else if(arguments.length > 1){
4575         cfg = Array.prototype.join.call(arguments, "");
4576     }
4577     
4578     
4579     if (typeof(cfg) == 'object') {
4580         Roo.apply(this,cfg)
4581     } else {
4582         // bc
4583         this.html = cfg;
4584     }
4585     if (this.url) {
4586         this.load();
4587     }
4588     
4589 };
4590 Roo.Template.prototype = {
4591     
4592     /**
4593      * @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..
4594      *                    it should be fixed so that template is observable...
4595      */
4596     url : false,
4597     /**
4598      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4599      */
4600     html : '',
4601     /**
4602      * Returns an HTML fragment of this template with the specified values applied.
4603      * @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'})
4604      * @return {String} The HTML fragment
4605      */
4606     applyTemplate : function(values){
4607         try {
4608            
4609             if(this.compiled){
4610                 return this.compiled(values);
4611             }
4612             var useF = this.disableFormats !== true;
4613             var fm = Roo.util.Format, tpl = this;
4614             var fn = function(m, name, format, args){
4615                 if(format && useF){
4616                     if(format.substr(0, 5) == "this."){
4617                         return tpl.call(format.substr(5), values[name], values);
4618                     }else{
4619                         if(args){
4620                             // quoted values are required for strings in compiled templates, 
4621                             // but for non compiled we need to strip them
4622                             // quoted reversed for jsmin
4623                             var re = /^\s*['"](.*)["']\s*$/;
4624                             args = args.split(',');
4625                             for(var i = 0, len = args.length; i < len; i++){
4626                                 args[i] = args[i].replace(re, "$1");
4627                             }
4628                             args = [values[name]].concat(args);
4629                         }else{
4630                             args = [values[name]];
4631                         }
4632                         return fm[format].apply(fm, args);
4633                     }
4634                 }else{
4635                     return values[name] !== undefined ? values[name] : "";
4636                 }
4637             };
4638             return this.html.replace(this.re, fn);
4639         } catch (e) {
4640             Roo.log(e);
4641             throw e;
4642         }
4643          
4644     },
4645     
4646     loading : false,
4647       
4648     load : function ()
4649     {
4650          
4651         if (this.loading) {
4652             return;
4653         }
4654         var _t = this;
4655         
4656         this.loading = true;
4657         this.compiled = false;
4658         
4659         var cx = new Roo.data.Connection();
4660         cx.request({
4661             url : this.url,
4662             method : 'GET',
4663             success : function (response) {
4664                 _t.loading = false;
4665                 _t.html = response.responseText;
4666                 _t.url = false;
4667                 _t.compile();
4668              },
4669             failure : function(response) {
4670                 Roo.log("Template failed to load from " + _t.url);
4671                 _t.loading = false;
4672             }
4673         });
4674     },
4675
4676     /**
4677      * Sets the HTML used as the template and optionally compiles it.
4678      * @param {String} html
4679      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4680      * @return {Roo.Template} this
4681      */
4682     set : function(html, compile){
4683         this.html = html;
4684         this.compiled = null;
4685         if(compile){
4686             this.compile();
4687         }
4688         return this;
4689     },
4690     
4691     /**
4692      * True to disable format functions (defaults to false)
4693      * @type Boolean
4694      */
4695     disableFormats : false,
4696     
4697     /**
4698     * The regular expression used to match template variables 
4699     * @type RegExp
4700     * @property 
4701     */
4702     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4703     
4704     /**
4705      * Compiles the template into an internal function, eliminating the RegEx overhead.
4706      * @return {Roo.Template} this
4707      */
4708     compile : function(){
4709         var fm = Roo.util.Format;
4710         var useF = this.disableFormats !== true;
4711         var sep = Roo.isGecko ? "+" : ",";
4712         var fn = function(m, name, format, args){
4713             if(format && useF){
4714                 args = args ? ',' + args : "";
4715                 if(format.substr(0, 5) != "this."){
4716                     format = "fm." + format + '(';
4717                 }else{
4718                     format = 'this.call("'+ format.substr(5) + '", ';
4719                     args = ", values";
4720                 }
4721             }else{
4722                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4723             }
4724             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4725         };
4726         var body;
4727         // branched to use + in gecko and [].join() in others
4728         if(Roo.isGecko){
4729             body = "this.compiled = function(values){ return '" +
4730                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4731                     "';};";
4732         }else{
4733             body = ["this.compiled = function(values){ return ['"];
4734             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4735             body.push("'].join('');};");
4736             body = body.join('');
4737         }
4738         /**
4739          * eval:var:values
4740          * eval:var:fm
4741          */
4742         eval(body);
4743         return this;
4744     },
4745     
4746     // private function used to call members
4747     call : function(fnName, value, allValues){
4748         return this[fnName](value, allValues);
4749     },
4750     
4751     /**
4752      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4753      * @param {String/HTMLElement/Roo.Element} el The context element
4754      * @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'})
4755      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4756      * @return {HTMLElement/Roo.Element} The new node or Element
4757      */
4758     insertFirst: function(el, values, returnElement){
4759         return this.doInsert('afterBegin', el, values, returnElement);
4760     },
4761
4762     /**
4763      * Applies the supplied values to the template and inserts the new node(s) before el.
4764      * @param {String/HTMLElement/Roo.Element} el The context element
4765      * @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'})
4766      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4767      * @return {HTMLElement/Roo.Element} The new node or Element
4768      */
4769     insertBefore: function(el, values, returnElement){
4770         return this.doInsert('beforeBegin', el, values, returnElement);
4771     },
4772
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) after el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @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'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertAfter : function(el, values, returnElement){
4781         return this.doInsert('afterEnd', el, values, returnElement);
4782     },
4783     
4784     /**
4785      * Applies the supplied values to the template and appends the new node(s) to el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @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'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     append : function(el, values, returnElement){
4792         return this.doInsert('beforeEnd', el, values, returnElement);
4793     },
4794
4795     doInsert : function(where, el, values, returnEl){
4796         el = Roo.getDom(el);
4797         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4798         return returnEl ? Roo.get(newNode, true) : newNode;
4799     },
4800
4801     /**
4802      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @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'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     overwrite : function(el, values, returnElement){
4809         el = Roo.getDom(el);
4810         el.innerHTML = this.applyTemplate(values);
4811         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4812     }
4813 };
4814 /**
4815  * Alias for {@link #applyTemplate}
4816  * @method
4817  */
4818 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4819
4820 // backwards compat
4821 Roo.DomHelper.Template = Roo.Template;
4822
4823 /**
4824  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4825  * @param {String/HTMLElement} el A DOM element or its id
4826  * @returns {Roo.Template} The created template
4827  * @static
4828  */
4829 Roo.Template.from = function(el){
4830     el = Roo.getDom(el);
4831     return new Roo.Template(el.value || el.innerHTML);
4832 };/*
4833  * Based on:
4834  * Ext JS Library 1.1.1
4835  * Copyright(c) 2006-2007, Ext JS, LLC.
4836  *
4837  * Originally Released Under LGPL - original licence link has changed is not relivant.
4838  *
4839  * Fork - LGPL
4840  * <script type="text/javascript">
4841  */
4842  
4843
4844 /*
4845  * This is code is also distributed under MIT license for use
4846  * with jQuery and prototype JavaScript libraries.
4847  */
4848 /**
4849  * @class Roo.DomQuery
4850 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).
4851 <p>
4852 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>
4853
4854 <p>
4855 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.
4856 </p>
4857 <h4>Element Selectors:</h4>
4858 <ul class="list">
4859     <li> <b>*</b> any element</li>
4860     <li> <b>E</b> an element with the tag E</li>
4861     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4862     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4863     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4864     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4865 </ul>
4866 <h4>Attribute Selectors:</h4>
4867 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4868 <ul class="list">
4869     <li> <b>E[foo]</b> has an attribute "foo"</li>
4870     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4871     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4872     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4873     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4874     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4875     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4876 </ul>
4877 <h4>Pseudo Classes:</h4>
4878 <ul class="list">
4879     <li> <b>E:first-child</b> E is the first child of its parent</li>
4880     <li> <b>E:last-child</b> E is the last child of its parent</li>
4881     <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>
4882     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4883     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4884     <li> <b>E:only-child</b> E is the only child of its parent</li>
4885     <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>
4886     <li> <b>E:first</b> the first E in the resultset</li>
4887     <li> <b>E:last</b> the last E in the resultset</li>
4888     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4889     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4890     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4891     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4892     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4893     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4894     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4895     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4896     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4897 </ul>
4898 <h4>CSS Value Selectors:</h4>
4899 <ul class="list">
4900     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4901     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4902     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4903     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4904     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4905     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4906 </ul>
4907  * @singleton
4908  */
4909 Roo.DomQuery = function(){
4910     var cache = {}, simpleCache = {}, valueCache = {};
4911     var nonSpace = /\S/;
4912     var trimRe = /^\s+|\s+$/g;
4913     var tplRe = /\{(\d+)\}/g;
4914     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4915     var tagTokenRe = /^(#)?([\w-\*]+)/;
4916     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4917
4918     function child(p, index){
4919         var i = 0;
4920         var n = p.firstChild;
4921         while(n){
4922             if(n.nodeType == 1){
4923                if(++i == index){
4924                    return n;
4925                }
4926             }
4927             n = n.nextSibling;
4928         }
4929         return null;
4930     };
4931
4932     function next(n){
4933         while((n = n.nextSibling) && n.nodeType != 1);
4934         return n;
4935     };
4936
4937     function prev(n){
4938         while((n = n.previousSibling) && n.nodeType != 1);
4939         return n;
4940     };
4941
4942     function children(d){
4943         var n = d.firstChild, ni = -1;
4944             while(n){
4945                 var nx = n.nextSibling;
4946                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4947                     d.removeChild(n);
4948                 }else{
4949                     n.nodeIndex = ++ni;
4950                 }
4951                 n = nx;
4952             }
4953             return this;
4954         };
4955
4956     function byClassName(c, a, v){
4957         if(!v){
4958             return c;
4959         }
4960         var r = [], ri = -1, cn;
4961         for(var i = 0, ci; ci = c[i]; i++){
4962             if((' '+ci.className+' ').indexOf(v) != -1){
4963                 r[++ri] = ci;
4964             }
4965         }
4966         return r;
4967     };
4968
4969     function attrValue(n, attr){
4970         if(!n.tagName && typeof n.length != "undefined"){
4971             n = n[0];
4972         }
4973         if(!n){
4974             return null;
4975         }
4976         if(attr == "for"){
4977             return n.htmlFor;
4978         }
4979         if(attr == "class" || attr == "className"){
4980             return n.className;
4981         }
4982         return n.getAttribute(attr) || n[attr];
4983
4984     };
4985
4986     function getNodes(ns, mode, tagName){
4987         var result = [], ri = -1, cs;
4988         if(!ns){
4989             return result;
4990         }
4991         tagName = tagName || "*";
4992         if(typeof ns.getElementsByTagName != "undefined"){
4993             ns = [ns];
4994         }
4995         if(!mode){
4996             for(var i = 0, ni; ni = ns[i]; i++){
4997                 cs = ni.getElementsByTagName(tagName);
4998                 for(var j = 0, ci; ci = cs[j]; j++){
4999                     result[++ri] = ci;
5000                 }
5001             }
5002         }else if(mode == "/" || mode == ">"){
5003             var utag = tagName.toUpperCase();
5004             for(var i = 0, ni, cn; ni = ns[i]; i++){
5005                 cn = ni.children || ni.childNodes;
5006                 for(var j = 0, cj; cj = cn[j]; j++){
5007                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5008                         result[++ri] = cj;
5009                     }
5010                 }
5011             }
5012         }else if(mode == "+"){
5013             var utag = tagName.toUpperCase();
5014             for(var i = 0, n; n = ns[i]; i++){
5015                 while((n = n.nextSibling) && n.nodeType != 1);
5016                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5017                     result[++ri] = n;
5018                 }
5019             }
5020         }else if(mode == "~"){
5021             for(var i = 0, n; n = ns[i]; i++){
5022                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5023                 if(n){
5024                     result[++ri] = n;
5025                 }
5026             }
5027         }
5028         return result;
5029     };
5030
5031     function concat(a, b){
5032         if(b.slice){
5033             return a.concat(b);
5034         }
5035         for(var i = 0, l = b.length; i < l; i++){
5036             a[a.length] = b[i];
5037         }
5038         return a;
5039     }
5040
5041     function byTag(cs, tagName){
5042         if(cs.tagName || cs == document){
5043             cs = [cs];
5044         }
5045         if(!tagName){
5046             return cs;
5047         }
5048         var r = [], ri = -1;
5049         tagName = tagName.toLowerCase();
5050         for(var i = 0, ci; ci = cs[i]; i++){
5051             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5052                 r[++ri] = ci;
5053             }
5054         }
5055         return r;
5056     };
5057
5058     function byId(cs, attr, id){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!id){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         for(var i = 0,ci; ci = cs[i]; i++){
5067             if(ci && ci.id == id){
5068                 r[++ri] = ci;
5069                 return r;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byAttribute(cs, attr, value, op, custom){
5076         var r = [], ri = -1, st = custom=="{";
5077         var f = Roo.DomQuery.operators[op];
5078         for(var i = 0, ci; ci = cs[i]; i++){
5079             var a;
5080             if(st){
5081                 a = Roo.DomQuery.getStyle(ci, attr);
5082             }
5083             else if(attr == "class" || attr == "className"){
5084                 a = ci.className;
5085             }else if(attr == "for"){
5086                 a = ci.htmlFor;
5087             }else if(attr == "href"){
5088                 a = ci.getAttribute("href", 2);
5089             }else{
5090                 a = ci.getAttribute(attr);
5091             }
5092             if((f && f(a, value)) || (!f && a)){
5093                 r[++ri] = ci;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byPseudo(cs, name, value){
5100         return Roo.DomQuery.pseudos[name](cs, value);
5101     };
5102
5103     // This is for IE MSXML which does not support expandos.
5104     // IE runs the same speed using setAttribute, however FF slows way down
5105     // and Safari completely fails so they need to continue to use expandos.
5106     var isIE = window.ActiveXObject ? true : false;
5107
5108     // this eval is stop the compressor from
5109     // renaming the variable to something shorter
5110     
5111     /** eval:var:batch */
5112     var batch = 30803; 
5113
5114     var key = 30803;
5115
5116     function nodupIEXml(cs){
5117         var d = ++key;
5118         cs[0].setAttribute("_nodup", d);
5119         var r = [cs[0]];
5120         for(var i = 1, len = cs.length; i < len; i++){
5121             var c = cs[i];
5122             if(!c.getAttribute("_nodup") != d){
5123                 c.setAttribute("_nodup", d);
5124                 r[r.length] = c;
5125             }
5126         }
5127         for(var i = 0, len = cs.length; i < len; i++){
5128             cs[i].removeAttribute("_nodup");
5129         }
5130         return r;
5131     }
5132
5133     function nodup(cs){
5134         if(!cs){
5135             return [];
5136         }
5137         var len = cs.length, c, i, r = cs, cj, ri = -1;
5138         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5139             return cs;
5140         }
5141         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5142             return nodupIEXml(cs);
5143         }
5144         var d = ++key;
5145         cs[0]._nodup = d;
5146         for(i = 1; c = cs[i]; i++){
5147             if(c._nodup != d){
5148                 c._nodup = d;
5149             }else{
5150                 r = [];
5151                 for(var j = 0; j < i; j++){
5152                     r[++ri] = cs[j];
5153                 }
5154                 for(j = i+1; cj = cs[j]; j++){
5155                     if(cj._nodup != d){
5156                         cj._nodup = d;
5157                         r[++ri] = cj;
5158                     }
5159                 }
5160                 return r;
5161             }
5162         }
5163         return r;
5164     }
5165
5166     function quickDiffIEXml(c1, c2){
5167         var d = ++key;
5168         for(var i = 0, len = c1.length; i < len; i++){
5169             c1[i].setAttribute("_qdiff", d);
5170         }
5171         var r = [];
5172         for(var i = 0, len = c2.length; i < len; i++){
5173             if(c2[i].getAttribute("_qdiff") != d){
5174                 r[r.length] = c2[i];
5175             }
5176         }
5177         for(var i = 0, len = c1.length; i < len; i++){
5178            c1[i].removeAttribute("_qdiff");
5179         }
5180         return r;
5181     }
5182
5183     function quickDiff(c1, c2){
5184         var len1 = c1.length;
5185         if(!len1){
5186             return c2;
5187         }
5188         if(isIE && c1[0].selectSingleNode){
5189             return quickDiffIEXml(c1, c2);
5190         }
5191         var d = ++key;
5192         for(var i = 0; i < len1; i++){
5193             c1[i]._qdiff = d;
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i]._qdiff != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         return r;
5202     }
5203
5204     function quickId(ns, mode, root, id){
5205         if(ns == root){
5206            var d = root.ownerDocument || root;
5207            return d.getElementById(id);
5208         }
5209         ns = getNodes(ns, mode, "*");
5210         return byId(ns, null, id);
5211     }
5212
5213     return {
5214         getStyle : function(el, name){
5215             return Roo.fly(el).getStyle(name);
5216         },
5217         /**
5218          * Compiles a selector/xpath query into a reusable function. The returned function
5219          * takes one parameter "root" (optional), which is the context node from where the query should start.
5220          * @param {String} selector The selector/xpath query
5221          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5222          * @return {Function}
5223          */
5224         compile : function(path, type){
5225             type = type || "select";
5226             
5227             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5228             var q = path, mode, lq;
5229             var tk = Roo.DomQuery.matchers;
5230             var tklen = tk.length;
5231             var mm;
5232
5233             // accept leading mode switch
5234             var lmode = q.match(modeRe);
5235             if(lmode && lmode[1]){
5236                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5237                 q = q.replace(lmode[1], "");
5238             }
5239             // strip leading slashes
5240             while(path.substr(0, 1)=="/"){
5241                 path = path.substr(1);
5242             }
5243
5244             while(q && lq != q){
5245                 lq = q;
5246                 var tm = q.match(tagTokenRe);
5247                 if(type == "select"){
5248                     if(tm){
5249                         if(tm[1] == "#"){
5250                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5251                         }else{
5252                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5253                         }
5254                         q = q.replace(tm[0], "");
5255                     }else if(q.substr(0, 1) != '@'){
5256                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5257                     }
5258                 }else{
5259                     if(tm){
5260                         if(tm[1] == "#"){
5261                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5262                         }else{
5263                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5264                         }
5265                         q = q.replace(tm[0], "");
5266                     }
5267                 }
5268                 while(!(mm = q.match(modeRe))){
5269                     var matched = false;
5270                     for(var j = 0; j < tklen; j++){
5271                         var t = tk[j];
5272                         var m = q.match(t.re);
5273                         if(m){
5274                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5275                                                     return m[i];
5276                                                 });
5277                             q = q.replace(m[0], "");
5278                             matched = true;
5279                             break;
5280                         }
5281                     }
5282                     // prevent infinite loop on bad selector
5283                     if(!matched){
5284                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5285                     }
5286                 }
5287                 if(mm[1]){
5288                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5289                     q = q.replace(mm[1], "");
5290                 }
5291             }
5292             fn[fn.length] = "return nodup(n);\n}";
5293             
5294              /** 
5295               * list of variables that need from compression as they are used by eval.
5296              *  eval:var:batch 
5297              *  eval:var:nodup
5298              *  eval:var:byTag
5299              *  eval:var:ById
5300              *  eval:var:getNodes
5301              *  eval:var:quickId
5302              *  eval:var:mode
5303              *  eval:var:root
5304              *  eval:var:n
5305              *  eval:var:byClassName
5306              *  eval:var:byPseudo
5307              *  eval:var:byAttribute
5308              *  eval:var:attrValue
5309              * 
5310              **/ 
5311             eval(fn.join(""));
5312             return f;
5313         },
5314
5315         /**
5316          * Selects a group of elements.
5317          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5318          * @param {Node} root (optional) The start of the query (defaults to document).
5319          * @return {Array}
5320          */
5321         select : function(path, root, type){
5322             if(!root || root == document){
5323                 root = document;
5324             }
5325             if(typeof root == "string"){
5326                 root = document.getElementById(root);
5327             }
5328             var paths = path.split(",");
5329             var results = [];
5330             for(var i = 0, len = paths.length; i < len; i++){
5331                 var p = paths[i].replace(trimRe, "");
5332                 if(!cache[p]){
5333                     cache[p] = Roo.DomQuery.compile(p);
5334                     if(!cache[p]){
5335                         throw p + " is not a valid selector";
5336                     }
5337                 }
5338                 var result = cache[p](root);
5339                 if(result && result != document){
5340                     results = results.concat(result);
5341                 }
5342             }
5343             if(paths.length > 1){
5344                 return nodup(results);
5345             }
5346             return results;
5347         },
5348
5349         /**
5350          * Selects a single element.
5351          * @param {String} selector The selector/xpath query
5352          * @param {Node} root (optional) The start of the query (defaults to document).
5353          * @return {Element}
5354          */
5355         selectNode : function(path, root){
5356             return Roo.DomQuery.select(path, root)[0];
5357         },
5358
5359         /**
5360          * Selects the value of a node, optionally replacing null with the defaultValue.
5361          * @param {String} selector The selector/xpath query
5362          * @param {Node} root (optional) The start of the query (defaults to document).
5363          * @param {String} defaultValue
5364          */
5365         selectValue : function(path, root, defaultValue){
5366             path = path.replace(trimRe, "");
5367             if(!valueCache[path]){
5368                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5369             }
5370             var n = valueCache[path](root);
5371             n = n[0] ? n[0] : n;
5372             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5373             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5374         },
5375
5376         /**
5377          * Selects the value of a node, parsing integers and floats.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {Number} defaultValue
5381          * @return {Number}
5382          */
5383         selectNumber : function(path, root, defaultValue){
5384             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5385             return parseFloat(v);
5386         },
5387
5388         /**
5389          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5390          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5391          * @param {String} selector The simple selector to test
5392          * @return {Boolean}
5393          */
5394         is : function(el, ss){
5395             if(typeof el == "string"){
5396                 el = document.getElementById(el);
5397             }
5398             var isArray = (el instanceof Array);
5399             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5400             return isArray ? (result.length == el.length) : (result.length > 0);
5401         },
5402
5403         /**
5404          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5405          * @param {Array} el An array of elements to filter
5406          * @param {String} selector The simple selector to test
5407          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5408          * the selector instead of the ones that match
5409          * @return {Array}
5410          */
5411         filter : function(els, ss, nonMatches){
5412             ss = ss.replace(trimRe, "");
5413             if(!simpleCache[ss]){
5414                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5415             }
5416             var result = simpleCache[ss](els);
5417             return nonMatches ? quickDiff(result, els) : result;
5418         },
5419
5420         /**
5421          * Collection of matching regular expressions and code snippets.
5422          */
5423         matchers : [{
5424                 re: /^\.([\w-]+)/,
5425                 select: 'n = byClassName(n, null, " {1} ");'
5426             }, {
5427                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5428                 select: 'n = byPseudo(n, "{1}", "{2}");'
5429             },{
5430                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5431                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5432             }, {
5433                 re: /^#([\w-]+)/,
5434                 select: 'n = byId(n, null, "{1}");'
5435             },{
5436                 re: /^@([\w-]+)/,
5437                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5438             }
5439         ],
5440
5441         /**
5442          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5443          * 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;.
5444          */
5445         operators : {
5446             "=" : function(a, v){
5447                 return a == v;
5448             },
5449             "!=" : function(a, v){
5450                 return a != v;
5451             },
5452             "^=" : function(a, v){
5453                 return a && a.substr(0, v.length) == v;
5454             },
5455             "$=" : function(a, v){
5456                 return a && a.substr(a.length-v.length) == v;
5457             },
5458             "*=" : function(a, v){
5459                 return a && a.indexOf(v) !== -1;
5460             },
5461             "%=" : function(a, v){
5462                 return (a % v) == 0;
5463             },
5464             "|=" : function(a, v){
5465                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5466             },
5467             "~=" : function(a, v){
5468                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5469             }
5470         },
5471
5472         /**
5473          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5474          * and the argument (if any) supplied in the selector.
5475          */
5476         pseudos : {
5477             "first-child" : function(c){
5478                 var r = [], ri = -1, n;
5479                 for(var i = 0, ci; ci = n = c[i]; i++){
5480                     while((n = n.previousSibling) && n.nodeType != 1);
5481                     if(!n){
5482                         r[++ri] = ci;
5483                     }
5484                 }
5485                 return r;
5486             },
5487
5488             "last-child" : function(c){
5489                 var r = [], ri = -1, n;
5490                 for(var i = 0, ci; ci = n = c[i]; i++){
5491                     while((n = n.nextSibling) && n.nodeType != 1);
5492                     if(!n){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "nth-child" : function(c, a) {
5500                 var r = [], ri = -1;
5501                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5502                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5503                 for(var i = 0, n; n = c[i]; i++){
5504                     var pn = n.parentNode;
5505                     if (batch != pn._batch) {
5506                         var j = 0;
5507                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5508                             if(cn.nodeType == 1){
5509                                cn.nodeIndex = ++j;
5510                             }
5511                         }
5512                         pn._batch = batch;
5513                     }
5514                     if (f == 1) {
5515                         if (l == 0 || n.nodeIndex == l){
5516                             r[++ri] = n;
5517                         }
5518                     } else if ((n.nodeIndex + l) % f == 0){
5519                         r[++ri] = n;
5520                     }
5521                 }
5522
5523                 return r;
5524             },
5525
5526             "only-child" : function(c){
5527                 var r = [], ri = -1;;
5528                 for(var i = 0, ci; ci = c[i]; i++){
5529                     if(!prev(ci) && !next(ci)){
5530                         r[++ri] = ci;
5531                     }
5532                 }
5533                 return r;
5534             },
5535
5536             "empty" : function(c){
5537                 var r = [], ri = -1;
5538                 for(var i = 0, ci; ci = c[i]; i++){
5539                     var cns = ci.childNodes, j = 0, cn, empty = true;
5540                     while(cn = cns[j]){
5541                         ++j;
5542                         if(cn.nodeType == 1 || cn.nodeType == 3){
5543                             empty = false;
5544                             break;
5545                         }
5546                     }
5547                     if(empty){
5548                         r[++ri] = ci;
5549                     }
5550                 }
5551                 return r;
5552             },
5553
5554             "contains" : function(c, v){
5555                 var r = [], ri = -1;
5556                 for(var i = 0, ci; ci = c[i]; i++){
5557                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5558                         r[++ri] = ci;
5559                     }
5560                 }
5561                 return r;
5562             },
5563
5564             "nodeValue" : function(c, v){
5565                 var r = [], ri = -1;
5566                 for(var i = 0, ci; ci = c[i]; i++){
5567                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5568                         r[++ri] = ci;
5569                     }
5570                 }
5571                 return r;
5572             },
5573
5574             "checked" : function(c){
5575                 var r = [], ri = -1;
5576                 for(var i = 0, ci; ci = c[i]; i++){
5577                     if(ci.checked == true){
5578                         r[++ri] = ci;
5579                     }
5580                 }
5581                 return r;
5582             },
5583
5584             "not" : function(c, ss){
5585                 return Roo.DomQuery.filter(c, ss, true);
5586             },
5587
5588             "odd" : function(c){
5589                 return this["nth-child"](c, "odd");
5590             },
5591
5592             "even" : function(c){
5593                 return this["nth-child"](c, "even");
5594             },
5595
5596             "nth" : function(c, a){
5597                 return c[a-1] || [];
5598             },
5599
5600             "first" : function(c){
5601                 return c[0] || [];
5602             },
5603
5604             "last" : function(c){
5605                 return c[c.length-1] || [];
5606             },
5607
5608             "has" : function(c, ss){
5609                 var s = Roo.DomQuery.select;
5610                 var r = [], ri = -1;
5611                 for(var i = 0, ci; ci = c[i]; i++){
5612                     if(s(ss, ci).length > 0){
5613                         r[++ri] = ci;
5614                     }
5615                 }
5616                 return r;
5617             },
5618
5619             "next" : function(c, ss){
5620                 var is = Roo.DomQuery.is;
5621                 var r = [], ri = -1;
5622                 for(var i = 0, ci; ci = c[i]; i++){
5623                     var n = next(ci);
5624                     if(n && is(n, ss)){
5625                         r[++ri] = ci;
5626                     }
5627                 }
5628                 return r;
5629             },
5630
5631             "prev" : function(c, ss){
5632                 var is = Roo.DomQuery.is;
5633                 var r = [], ri = -1;
5634                 for(var i = 0, ci; ci = c[i]; i++){
5635                     var n = prev(ci);
5636                     if(n && is(n, ss)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             }
5642         }
5643     };
5644 }();
5645
5646 /**
5647  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5648  * @param {String} path The selector/xpath query
5649  * @param {Node} root (optional) The start of the query (defaults to document).
5650  * @return {Array}
5651  * @member Roo
5652  * @method query
5653  */
5654 Roo.query = Roo.DomQuery.select;
5655 /*
5656  * Based on:
5657  * Ext JS Library 1.1.1
5658  * Copyright(c) 2006-2007, Ext JS, LLC.
5659  *
5660  * Originally Released Under LGPL - original licence link has changed is not relivant.
5661  *
5662  * Fork - LGPL
5663  * <script type="text/javascript">
5664  */
5665
5666 /**
5667  * @class Roo.util.Observable
5668  * Base class that provides a common interface for publishing events. Subclasses are expected to
5669  * to have a property "events" with all the events defined.<br>
5670  * For example:
5671  * <pre><code>
5672  Employee = function(name){
5673     this.name = name;
5674     this.addEvents({
5675         "fired" : true,
5676         "quit" : true
5677     });
5678  }
5679  Roo.extend(Employee, Roo.util.Observable);
5680 </code></pre>
5681  * @param {Object} config properties to use (incuding events / listeners)
5682  */
5683
5684 Roo.util.Observable = function(cfg){
5685     
5686     cfg = cfg|| {};
5687     this.addEvents(cfg.events || {});
5688     if (cfg.events) {
5689         delete cfg.events; // make sure
5690     }
5691      
5692     Roo.apply(this, cfg);
5693     
5694     if(this.listeners){
5695         this.on(this.listeners);
5696         delete this.listeners;
5697     }
5698 };
5699 Roo.util.Observable.prototype = {
5700     /** 
5701  * @cfg {Object} listeners  list of events and functions to call for this object, 
5702  * For example :
5703  * <pre><code>
5704     listeners :  { 
5705        'click' : function(e) {
5706            ..... 
5707         } ,
5708         .... 
5709     } 
5710   </code></pre>
5711  */
5712     
5713     
5714     /**
5715      * Fires the specified event with the passed parameters (minus the event name).
5716      * @param {String} eventName
5717      * @param {Object...} args Variable number of parameters are passed to handlers
5718      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5719      */
5720     fireEvent : function(){
5721         var ce = this.events[arguments[0].toLowerCase()];
5722         if(typeof ce == "object"){
5723             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5724         }else{
5725             return true;
5726         }
5727     },
5728
5729     // private
5730     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5731
5732     /**
5733      * Appends an event handler to this component
5734      * @param {String}   eventName The type of event to listen for
5735      * @param {Function} handler The method the event invokes
5736      * @param {Object}   scope (optional) The scope in which to execute the handler
5737      * function. The handler function's "this" context.
5738      * @param {Object}   options (optional) An object containing handler configuration
5739      * properties. This may contain any of the following properties:<ul>
5740      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5741      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5742      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5743      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5744      * by the specified number of milliseconds. If the event fires again within that time, the original
5745      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5746      * </ul><br>
5747      * <p>
5748      * <b>Combining Options</b><br>
5749      * Using the options argument, it is possible to combine different types of listeners:<br>
5750      * <br>
5751      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5752                 <pre><code>
5753                 el.on('click', this.onClick, this, {
5754                         single: true,
5755                 delay: 100,
5756                 forumId: 4
5757                 });
5758                 </code></pre>
5759      * <p>
5760      * <b>Attaching multiple handlers in 1 call</b><br>
5761      * The method also allows for a single argument to be passed which is a config object containing properties
5762      * which specify multiple handlers.
5763      * <pre><code>
5764                 el.on({
5765                         'click': {
5766                         fn: this.onClick,
5767                         scope: this,
5768                         delay: 100
5769                 }, 
5770                 'mouseover': {
5771                         fn: this.onMouseOver,
5772                         scope: this
5773                 },
5774                 'mouseout': {
5775                         fn: this.onMouseOut,
5776                         scope: this
5777                 }
5778                 });
5779                 </code></pre>
5780      * <p>
5781      * Or a shorthand syntax which passes the same scope object to all handlers:
5782         <pre><code>
5783                 el.on({
5784                         'click': this.onClick,
5785                 'mouseover': this.onMouseOver,
5786                 'mouseout': this.onMouseOut,
5787                 scope: this
5788                 });
5789                 </code></pre>
5790      */
5791     addListener : function(eventName, fn, scope, o){
5792         if(typeof eventName == "object"){
5793             o = eventName;
5794             for(var e in o){
5795                 if(this.filterOptRe.test(e)){
5796                     continue;
5797                 }
5798                 if(typeof o[e] == "function"){
5799                     // shared options
5800                     this.addListener(e, o[e], o.scope,  o);
5801                 }else{
5802                     // individual options
5803                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5804                 }
5805             }
5806             return;
5807         }
5808         o = (!o || typeof o == "boolean") ? {} : o;
5809         eventName = eventName.toLowerCase();
5810         var ce = this.events[eventName] || true;
5811         if(typeof ce == "boolean"){
5812             ce = new Roo.util.Event(this, eventName);
5813             this.events[eventName] = ce;
5814         }
5815         ce.addListener(fn, scope, o);
5816     },
5817
5818     /**
5819      * Removes a listener
5820      * @param {String}   eventName     The type of event to listen for
5821      * @param {Function} handler        The handler to remove
5822      * @param {Object}   scope  (optional) The scope (this object) for the handler
5823      */
5824     removeListener : function(eventName, fn, scope){
5825         var ce = this.events[eventName.toLowerCase()];
5826         if(typeof ce == "object"){
5827             ce.removeListener(fn, scope);
5828         }
5829     },
5830
5831     /**
5832      * Removes all listeners for this object
5833      */
5834     purgeListeners : function(){
5835         for(var evt in this.events){
5836             if(typeof this.events[evt] == "object"){
5837                  this.events[evt].clearListeners();
5838             }
5839         }
5840     },
5841
5842     relayEvents : function(o, events){
5843         var createHandler = function(ename){
5844             return function(){
5845                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5846             };
5847         };
5848         for(var i = 0, len = events.length; i < len; i++){
5849             var ename = events[i];
5850             if(!this.events[ename]){ this.events[ename] = true; };
5851             o.on(ename, createHandler(ename), this);
5852         }
5853     },
5854
5855     /**
5856      * Used to define events on this Observable
5857      * @param {Object} object The object with the events defined
5858      */
5859     addEvents : function(o){
5860         if(!this.events){
5861             this.events = {};
5862         }
5863         Roo.applyIf(this.events, o);
5864     },
5865
5866     /**
5867      * Checks to see if this object has any listeners for a specified event
5868      * @param {String} eventName The name of the event to check for
5869      * @return {Boolean} True if the event is being listened for, else false
5870      */
5871     hasListener : function(eventName){
5872         var e = this.events[eventName];
5873         return typeof e == "object" && e.listeners.length > 0;
5874     }
5875 };
5876 /**
5877  * Appends an event handler to this element (shorthand for addListener)
5878  * @param {String}   eventName     The type of event to listen for
5879  * @param {Function} handler        The method the event invokes
5880  * @param {Object}   scope (optional) The scope in which to execute the handler
5881  * function. The handler function's "this" context.
5882  * @param {Object}   options  (optional)
5883  * @method
5884  */
5885 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5886 /**
5887  * Removes a listener (shorthand for removeListener)
5888  * @param {String}   eventName     The type of event to listen for
5889  * @param {Function} handler        The handler to remove
5890  * @param {Object}   scope  (optional) The scope (this object) for the handler
5891  * @method
5892  */
5893 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5894
5895 /**
5896  * Starts capture on the specified Observable. All events will be passed
5897  * to the supplied function with the event name + standard signature of the event
5898  * <b>before</b> the event is fired. If the supplied function returns false,
5899  * the event will not fire.
5900  * @param {Observable} o The Observable to capture
5901  * @param {Function} fn The function to call
5902  * @param {Object} scope (optional) The scope (this object) for the fn
5903  * @static
5904  */
5905 Roo.util.Observable.capture = function(o, fn, scope){
5906     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5907 };
5908
5909 /**
5910  * Removes <b>all</b> added captures from the Observable.
5911  * @param {Observable} o The Observable to release
5912  * @static
5913  */
5914 Roo.util.Observable.releaseCapture = function(o){
5915     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5916 };
5917
5918 (function(){
5919
5920     var createBuffered = function(h, o, scope){
5921         var task = new Roo.util.DelayedTask();
5922         return function(){
5923             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5924         };
5925     };
5926
5927     var createSingle = function(h, e, fn, scope){
5928         return function(){
5929             e.removeListener(fn, scope);
5930             return h.apply(scope, arguments);
5931         };
5932     };
5933
5934     var createDelayed = function(h, o, scope){
5935         return function(){
5936             var args = Array.prototype.slice.call(arguments, 0);
5937             setTimeout(function(){
5938                 h.apply(scope, args);
5939             }, o.delay || 10);
5940         };
5941     };
5942
5943     Roo.util.Event = function(obj, name){
5944         this.name = name;
5945         this.obj = obj;
5946         this.listeners = [];
5947     };
5948
5949     Roo.util.Event.prototype = {
5950         addListener : function(fn, scope, options){
5951             var o = options || {};
5952             scope = scope || this.obj;
5953             if(!this.isListening(fn, scope)){
5954                 var l = {fn: fn, scope: scope, options: o};
5955                 var h = fn;
5956                 if(o.delay){
5957                     h = createDelayed(h, o, scope);
5958                 }
5959                 if(o.single){
5960                     h = createSingle(h, this, fn, scope);
5961                 }
5962                 if(o.buffer){
5963                     h = createBuffered(h, o, scope);
5964                 }
5965                 l.fireFn = h;
5966                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5967                     this.listeners.push(l);
5968                 }else{
5969                     this.listeners = this.listeners.slice(0);
5970                     this.listeners.push(l);
5971                 }
5972             }
5973         },
5974
5975         findListener : function(fn, scope){
5976             scope = scope || this.obj;
5977             var ls = this.listeners;
5978             for(var i = 0, len = ls.length; i < len; i++){
5979                 var l = ls[i];
5980                 if(l.fn == fn && l.scope == scope){
5981                     return i;
5982                 }
5983             }
5984             return -1;
5985         },
5986
5987         isListening : function(fn, scope){
5988             return this.findListener(fn, scope) != -1;
5989         },
5990
5991         removeListener : function(fn, scope){
5992             var index;
5993             if((index = this.findListener(fn, scope)) != -1){
5994                 if(!this.firing){
5995                     this.listeners.splice(index, 1);
5996                 }else{
5997                     this.listeners = this.listeners.slice(0);
5998                     this.listeners.splice(index, 1);
5999                 }
6000                 return true;
6001             }
6002             return false;
6003         },
6004
6005         clearListeners : function(){
6006             this.listeners = [];
6007         },
6008
6009         fire : function(){
6010             var ls = this.listeners, scope, len = ls.length;
6011             if(len > 0){
6012                 this.firing = true;
6013                 var args = Array.prototype.slice.call(arguments, 0);
6014                 for(var i = 0; i < len; i++){
6015                     var l = ls[i];
6016                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6017                         this.firing = false;
6018                         return false;
6019                     }
6020                 }
6021                 this.firing = false;
6022             }
6023             return true;
6024         }
6025     };
6026 })();/*
6027  * Based on:
6028  * Ext JS Library 1.1.1
6029  * Copyright(c) 2006-2007, Ext JS, LLC.
6030  *
6031  * Originally Released Under LGPL - original licence link has changed is not relivant.
6032  *
6033  * Fork - LGPL
6034  * <script type="text/javascript">
6035  */
6036
6037 /**
6038  * @class Roo.EventManager
6039  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6040  * several useful events directly.
6041  * See {@link Roo.EventObject} for more details on normalized event objects.
6042  * @singleton
6043  */
6044 Roo.EventManager = function(){
6045     var docReadyEvent, docReadyProcId, docReadyState = false;
6046     var resizeEvent, resizeTask, textEvent, textSize;
6047     var E = Roo.lib.Event;
6048     var D = Roo.lib.Dom;
6049
6050     
6051     
6052
6053     var fireDocReady = function(){
6054         if(!docReadyState){
6055             docReadyState = true;
6056             Roo.isReady = true;
6057             if(docReadyProcId){
6058                 clearInterval(docReadyProcId);
6059             }
6060             if(Roo.isGecko || Roo.isOpera) {
6061                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6062             }
6063             if(Roo.isIE){
6064                 var defer = document.getElementById("ie-deferred-loader");
6065                 if(defer){
6066                     defer.onreadystatechange = null;
6067                     defer.parentNode.removeChild(defer);
6068                 }
6069             }
6070             if(docReadyEvent){
6071                 docReadyEvent.fire();
6072                 docReadyEvent.clearListeners();
6073             }
6074         }
6075     };
6076     
6077     var initDocReady = function(){
6078         docReadyEvent = new Roo.util.Event();
6079         if(Roo.isGecko || Roo.isOpera) {
6080             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6081         }else if(Roo.isIE){
6082             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6083             var defer = document.getElementById("ie-deferred-loader");
6084             defer.onreadystatechange = function(){
6085                 if(this.readyState == "complete"){
6086                     fireDocReady();
6087                 }
6088             };
6089         }else if(Roo.isSafari){ 
6090             docReadyProcId = setInterval(function(){
6091                 var rs = document.readyState;
6092                 if(rs == "complete") {
6093                     fireDocReady();     
6094                  }
6095             }, 10);
6096         }
6097         // no matter what, make sure it fires on load
6098         E.on(window, "load", fireDocReady);
6099     };
6100
6101     var createBuffered = function(h, o){
6102         var task = new Roo.util.DelayedTask(h);
6103         return function(e){
6104             // create new event object impl so new events don't wipe out properties
6105             e = new Roo.EventObjectImpl(e);
6106             task.delay(o.buffer, h, null, [e]);
6107         };
6108     };
6109
6110     var createSingle = function(h, el, ename, fn){
6111         return function(e){
6112             Roo.EventManager.removeListener(el, ename, fn);
6113             h(e);
6114         };
6115     };
6116
6117     var createDelayed = function(h, o){
6118         return function(e){
6119             // create new event object impl so new events don't wipe out properties
6120             e = new Roo.EventObjectImpl(e);
6121             setTimeout(function(){
6122                 h(e);
6123             }, o.delay || 10);
6124         };
6125     };
6126     var transitionEndVal = false;
6127     
6128     var transitionEnd = function()
6129     {
6130         if (transitionEndVal) {
6131             return transitionEndVal;
6132         }
6133         var el = document.createElement('div');
6134
6135         var transEndEventNames = {
6136             WebkitTransition : 'webkitTransitionEnd',
6137             MozTransition    : 'transitionend',
6138             OTransition      : 'oTransitionEnd otransitionend',
6139             transition       : 'transitionend'
6140         };
6141     
6142         for (var name in transEndEventNames) {
6143             if (el.style[name] !== undefined) {
6144                 transitionEndVal = transEndEventNames[name];
6145                 return  transitionEndVal ;
6146             }
6147         }
6148     }
6149     
6150
6151     var listen = function(element, ename, opt, fn, scope){
6152         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6153         fn = fn || o.fn; scope = scope || o.scope;
6154         var el = Roo.getDom(element);
6155         
6156         
6157         if(!el){
6158             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6159         }
6160         
6161         if (ename == 'transitionend') {
6162             ename = transitionEnd();
6163         }
6164         var h = function(e){
6165             e = Roo.EventObject.setEvent(e);
6166             var t;
6167             if(o.delegate){
6168                 t = e.getTarget(o.delegate, el);
6169                 if(!t){
6170                     return;
6171                 }
6172             }else{
6173                 t = e.target;
6174             }
6175             if(o.stopEvent === true){
6176                 e.stopEvent();
6177             }
6178             if(o.preventDefault === true){
6179                e.preventDefault();
6180             }
6181             if(o.stopPropagation === true){
6182                 e.stopPropagation();
6183             }
6184
6185             if(o.normalized === false){
6186                 e = e.browserEvent;
6187             }
6188
6189             fn.call(scope || el, e, t, o);
6190         };
6191         if(o.delay){
6192             h = createDelayed(h, o);
6193         }
6194         if(o.single){
6195             h = createSingle(h, el, ename, fn);
6196         }
6197         if(o.buffer){
6198             h = createBuffered(h, o);
6199         }
6200         fn._handlers = fn._handlers || [];
6201         
6202         
6203         fn._handlers.push([Roo.id(el), ename, h]);
6204         
6205         
6206          
6207         E.on(el, ename, h);
6208         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6209             el.addEventListener("DOMMouseScroll", h, false);
6210             E.on(window, 'unload', function(){
6211                 el.removeEventListener("DOMMouseScroll", h, false);
6212             });
6213         }
6214         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6215             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6216         }
6217         return h;
6218     };
6219
6220     var stopListening = function(el, ename, fn){
6221         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6222         if(hds){
6223             for(var i = 0, len = hds.length; i < len; i++){
6224                 var h = hds[i];
6225                 if(h[0] == id && h[1] == ename){
6226                     hd = h[2];
6227                     hds.splice(i, 1);
6228                     break;
6229                 }
6230             }
6231         }
6232         E.un(el, ename, hd);
6233         el = Roo.getDom(el);
6234         if(ename == "mousewheel" && el.addEventListener){
6235             el.removeEventListener("DOMMouseScroll", hd, false);
6236         }
6237         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6238             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6239         }
6240     };
6241
6242     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6243     
6244     var pub = {
6245         
6246         
6247         /** 
6248          * Fix for doc tools
6249          * @scope Roo.EventManager
6250          */
6251         
6252         
6253         /** 
6254          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6255          * object with a Roo.EventObject
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An object that becomes the scope of the handler
6258          * @param {boolean}  override If true, the obj passed in becomes
6259          *                             the execution scope of the listener
6260          * @return {Function} The wrapped function
6261          * @deprecated
6262          */
6263         wrap : function(fn, scope, override){
6264             return function(e){
6265                 Roo.EventObject.setEvent(e);
6266                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6267             };
6268         },
6269         
6270         /**
6271      * Appends an event handler to an element (shorthand for addListener)
6272      * @param {String/HTMLElement}   element        The html element or id to assign the
6273      * @param {String}   eventName The type of event to listen for
6274      * @param {Function} handler The method the event invokes
6275      * @param {Object}   scope (optional) The scope in which to execute the handler
6276      * function. The handler function's "this" context.
6277      * @param {Object}   options (optional) An object containing handler configuration
6278      * properties. This may contain any of the following properties:<ul>
6279      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6280      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6281      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6282      * <li>preventDefault {Boolean} True to prevent the default action</li>
6283      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6284      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6285      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6286      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6287      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6288      * by the specified number of milliseconds. If the event fires again within that time, the original
6289      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6290      * </ul><br>
6291      * <p>
6292      * <b>Combining Options</b><br>
6293      * Using the options argument, it is possible to combine different types of listeners:<br>
6294      * <br>
6295      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6296      * Code:<pre><code>
6297 el.on('click', this.onClick, this, {
6298     single: true,
6299     delay: 100,
6300     stopEvent : true,
6301     forumId: 4
6302 });</code></pre>
6303      * <p>
6304      * <b>Attaching multiple handlers in 1 call</b><br>
6305       * The method also allows for a single argument to be passed which is a config object containing properties
6306      * which specify multiple handlers.
6307      * <p>
6308      * Code:<pre><code>
6309 el.on({
6310     'click' : {
6311         fn: this.onClick
6312         scope: this,
6313         delay: 100
6314     },
6315     'mouseover' : {
6316         fn: this.onMouseOver
6317         scope: this
6318     },
6319     'mouseout' : {
6320         fn: this.onMouseOut
6321         scope: this
6322     }
6323 });</code></pre>
6324      * <p>
6325      * Or a shorthand syntax:<br>
6326      * Code:<pre><code>
6327 el.on({
6328     'click' : this.onClick,
6329     'mouseover' : this.onMouseOver,
6330     'mouseout' : this.onMouseOut
6331     scope: this
6332 });</code></pre>
6333      */
6334         addListener : function(element, eventName, fn, scope, options){
6335             if(typeof eventName == "object"){
6336                 var o = eventName;
6337                 for(var e in o){
6338                     if(propRe.test(e)){
6339                         continue;
6340                     }
6341                     if(typeof o[e] == "function"){
6342                         // shared options
6343                         listen(element, e, o, o[e], o.scope);
6344                     }else{
6345                         // individual options
6346                         listen(element, e, o[e]);
6347                     }
6348                 }
6349                 return;
6350             }
6351             return listen(element, eventName, options, fn, scope);
6352         },
6353         
6354         /**
6355          * Removes an event handler
6356          *
6357          * @param {String/HTMLElement}   element        The id or html element to remove the 
6358          *                             event from
6359          * @param {String}   eventName     The type of event
6360          * @param {Function} fn
6361          * @return {Boolean} True if a listener was actually removed
6362          */
6363         removeListener : function(element, eventName, fn){
6364             return stopListening(element, eventName, fn);
6365         },
6366         
6367         /**
6368          * Fires when the document is ready (before onload and before images are loaded). Can be 
6369          * accessed shorthanded Roo.onReady().
6370          * @param {Function} fn        The method the event invokes
6371          * @param {Object}   scope    An  object that becomes the scope of the handler
6372          * @param {boolean}  options
6373          */
6374         onDocumentReady : function(fn, scope, options){
6375             if(docReadyState){ // if it already fired
6376                 docReadyEvent.addListener(fn, scope, options);
6377                 docReadyEvent.fire();
6378                 docReadyEvent.clearListeners();
6379                 return;
6380             }
6381             if(!docReadyEvent){
6382                 initDocReady();
6383             }
6384             docReadyEvent.addListener(fn, scope, options);
6385         },
6386         
6387         /**
6388          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6389          * @param {Function} fn        The method the event invokes
6390          * @param {Object}   scope    An object that becomes the scope of the handler
6391          * @param {boolean}  options
6392          */
6393         onWindowResize : function(fn, scope, options){
6394             if(!resizeEvent){
6395                 resizeEvent = new Roo.util.Event();
6396                 resizeTask = new Roo.util.DelayedTask(function(){
6397                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398                 });
6399                 E.on(window, "resize", function(){
6400                     if(Roo.isIE){
6401                         resizeTask.delay(50);
6402                     }else{
6403                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6404                     }
6405                 });
6406             }
6407             resizeEvent.addListener(fn, scope, options);
6408         },
6409
6410         /**
6411          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6412          * @param {Function} fn        The method the event invokes
6413          * @param {Object}   scope    An object that becomes the scope of the handler
6414          * @param {boolean}  options
6415          */
6416         onTextResize : function(fn, scope, options){
6417             if(!textEvent){
6418                 textEvent = new Roo.util.Event();
6419                 var textEl = new Roo.Element(document.createElement('div'));
6420                 textEl.dom.className = 'x-text-resize';
6421                 textEl.dom.innerHTML = 'X';
6422                 textEl.appendTo(document.body);
6423                 textSize = textEl.dom.offsetHeight;
6424                 setInterval(function(){
6425                     if(textEl.dom.offsetHeight != textSize){
6426                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6427                     }
6428                 }, this.textResizeInterval);
6429             }
6430             textEvent.addListener(fn, scope, options);
6431         },
6432
6433         /**
6434          * Removes the passed window resize listener.
6435          * @param {Function} fn        The method the event invokes
6436          * @param {Object}   scope    The scope of handler
6437          */
6438         removeResizeListener : function(fn, scope){
6439             if(resizeEvent){
6440                 resizeEvent.removeListener(fn, scope);
6441             }
6442         },
6443
6444         // private
6445         fireResize : function(){
6446             if(resizeEvent){
6447                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6448             }   
6449         },
6450         /**
6451          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6452          */
6453         ieDeferSrc : false,
6454         /**
6455          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6456          */
6457         textResizeInterval : 50
6458     };
6459     
6460     /**
6461      * Fix for doc tools
6462      * @scopeAlias pub=Roo.EventManager
6463      */
6464     
6465      /**
6466      * Appends an event handler to an element (shorthand for addListener)
6467      * @param {String/HTMLElement}   element        The html element or id to assign the
6468      * @param {String}   eventName The type of event to listen for
6469      * @param {Function} handler The method the event invokes
6470      * @param {Object}   scope (optional) The scope in which to execute the handler
6471      * function. The handler function's "this" context.
6472      * @param {Object}   options (optional) An object containing handler configuration
6473      * properties. This may contain any of the following properties:<ul>
6474      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6475      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6476      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6477      * <li>preventDefault {Boolean} True to prevent the default action</li>
6478      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6479      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6480      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6481      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6482      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6483      * by the specified number of milliseconds. If the event fires again within that time, the original
6484      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6485      * </ul><br>
6486      * <p>
6487      * <b>Combining Options</b><br>
6488      * Using the options argument, it is possible to combine different types of listeners:<br>
6489      * <br>
6490      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6491      * Code:<pre><code>
6492 el.on('click', this.onClick, this, {
6493     single: true,
6494     delay: 100,
6495     stopEvent : true,
6496     forumId: 4
6497 });</code></pre>
6498      * <p>
6499      * <b>Attaching multiple handlers in 1 call</b><br>
6500       * The method also allows for a single argument to be passed which is a config object containing properties
6501      * which specify multiple handlers.
6502      * <p>
6503      * Code:<pre><code>
6504 el.on({
6505     'click' : {
6506         fn: this.onClick
6507         scope: this,
6508         delay: 100
6509     },
6510     'mouseover' : {
6511         fn: this.onMouseOver
6512         scope: this
6513     },
6514     'mouseout' : {
6515         fn: this.onMouseOut
6516         scope: this
6517     }
6518 });</code></pre>
6519      * <p>
6520      * Or a shorthand syntax:<br>
6521      * Code:<pre><code>
6522 el.on({
6523     'click' : this.onClick,
6524     'mouseover' : this.onMouseOver,
6525     'mouseout' : this.onMouseOut
6526     scope: this
6527 });</code></pre>
6528      */
6529     pub.on = pub.addListener;
6530     pub.un = pub.removeListener;
6531
6532     pub.stoppedMouseDownEvent = new Roo.util.Event();
6533     return pub;
6534 }();
6535 /**
6536   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6537   * @param {Function} fn        The method the event invokes
6538   * @param {Object}   scope    An  object that becomes the scope of the handler
6539   * @param {boolean}  override If true, the obj passed in becomes
6540   *                             the execution scope of the listener
6541   * @member Roo
6542   * @method onReady
6543  */
6544 Roo.onReady = Roo.EventManager.onDocumentReady;
6545
6546 Roo.onReady(function(){
6547     var bd = Roo.get(document.body);
6548     if(!bd){ return; }
6549
6550     var cls = [
6551             Roo.isIE ? "roo-ie"
6552             : Roo.isGecko ? "roo-gecko"
6553             : Roo.isOpera ? "roo-opera"
6554             : Roo.isSafari ? "roo-safari" : ""];
6555
6556     if(Roo.isMac){
6557         cls.push("roo-mac");
6558     }
6559     if(Roo.isLinux){
6560         cls.push("roo-linux");
6561     }
6562     if(Roo.isBorderBox){
6563         cls.push('roo-border-box');
6564     }
6565     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6566         var p = bd.dom.parentNode;
6567         if(p){
6568             p.className += ' roo-strict';
6569         }
6570     }
6571     bd.addClass(cls.join(' '));
6572 });
6573
6574 /**
6575  * @class Roo.EventObject
6576  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6577  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6578  * Example:
6579  * <pre><code>
6580  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6581     e.preventDefault();
6582     var target = e.getTarget();
6583     ...
6584  }
6585  var myDiv = Roo.get("myDiv");
6586  myDiv.on("click", handleClick);
6587  //or
6588  Roo.EventManager.on("myDiv", 'click', handleClick);
6589  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6590  </code></pre>
6591  * @singleton
6592  */
6593 Roo.EventObject = function(){
6594     
6595     var E = Roo.lib.Event;
6596     
6597     // safari keypress events for special keys return bad keycodes
6598     var safariKeys = {
6599         63234 : 37, // left
6600         63235 : 39, // right
6601         63232 : 38, // up
6602         63233 : 40, // down
6603         63276 : 33, // page up
6604         63277 : 34, // page down
6605         63272 : 46, // delete
6606         63273 : 36, // home
6607         63275 : 35  // end
6608     };
6609
6610     // normalize button clicks
6611     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6612                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6613
6614     Roo.EventObjectImpl = function(e){
6615         if(e){
6616             this.setEvent(e.browserEvent || e);
6617         }
6618     };
6619     Roo.EventObjectImpl.prototype = {
6620         /**
6621          * Used to fix doc tools.
6622          * @scope Roo.EventObject.prototype
6623          */
6624             
6625
6626         
6627         
6628         /** The normal browser event */
6629         browserEvent : null,
6630         /** The button pressed in a mouse event */
6631         button : -1,
6632         /** True if the shift key was down during the event */
6633         shiftKey : false,
6634         /** True if the control key was down during the event */
6635         ctrlKey : false,
6636         /** True if the alt key was down during the event */
6637         altKey : false,
6638
6639         /** Key constant 
6640         * @type Number */
6641         BACKSPACE : 8,
6642         /** Key constant 
6643         * @type Number */
6644         TAB : 9,
6645         /** Key constant 
6646         * @type Number */
6647         RETURN : 13,
6648         /** Key constant 
6649         * @type Number */
6650         ENTER : 13,
6651         /** Key constant 
6652         * @type Number */
6653         SHIFT : 16,
6654         /** Key constant 
6655         * @type Number */
6656         CONTROL : 17,
6657         /** Key constant 
6658         * @type Number */
6659         ESC : 27,
6660         /** Key constant 
6661         * @type Number */
6662         SPACE : 32,
6663         /** Key constant 
6664         * @type Number */
6665         PAGEUP : 33,
6666         /** Key constant 
6667         * @type Number */
6668         PAGEDOWN : 34,
6669         /** Key constant 
6670         * @type Number */
6671         END : 35,
6672         /** Key constant 
6673         * @type Number */
6674         HOME : 36,
6675         /** Key constant 
6676         * @type Number */
6677         LEFT : 37,
6678         /** Key constant 
6679         * @type Number */
6680         UP : 38,
6681         /** Key constant 
6682         * @type Number */
6683         RIGHT : 39,
6684         /** Key constant 
6685         * @type Number */
6686         DOWN : 40,
6687         /** Key constant 
6688         * @type Number */
6689         DELETE : 46,
6690         /** Key constant 
6691         * @type Number */
6692         F5 : 116,
6693
6694            /** @private */
6695         setEvent : function(e){
6696             if(e == this || (e && e.browserEvent)){ // already wrapped
6697                 return e;
6698             }
6699             this.browserEvent = e;
6700             if(e){
6701                 // normalize buttons
6702                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6703                 if(e.type == 'click' && this.button == -1){
6704                     this.button = 0;
6705                 }
6706                 this.type = e.type;
6707                 this.shiftKey = e.shiftKey;
6708                 // mac metaKey behaves like ctrlKey
6709                 this.ctrlKey = e.ctrlKey || e.metaKey;
6710                 this.altKey = e.altKey;
6711                 // in getKey these will be normalized for the mac
6712                 this.keyCode = e.keyCode;
6713                 // keyup warnings on firefox.
6714                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6715                 // cache the target for the delayed and or buffered events
6716                 this.target = E.getTarget(e);
6717                 // same for XY
6718                 this.xy = E.getXY(e);
6719             }else{
6720                 this.button = -1;
6721                 this.shiftKey = false;
6722                 this.ctrlKey = false;
6723                 this.altKey = false;
6724                 this.keyCode = 0;
6725                 this.charCode =0;
6726                 this.target = null;
6727                 this.xy = [0, 0];
6728             }
6729             return this;
6730         },
6731
6732         /**
6733          * Stop the event (preventDefault and stopPropagation)
6734          */
6735         stopEvent : function(){
6736             if(this.browserEvent){
6737                 if(this.browserEvent.type == 'mousedown'){
6738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6739                 }
6740                 E.stopEvent(this.browserEvent);
6741             }
6742         },
6743
6744         /**
6745          * Prevents the browsers default handling of the event.
6746          */
6747         preventDefault : function(){
6748             if(this.browserEvent){
6749                 E.preventDefault(this.browserEvent);
6750             }
6751         },
6752
6753         /** @private */
6754         isNavKeyPress : function(){
6755             var k = this.keyCode;
6756             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6757             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6758         },
6759
6760         isSpecialKey : function(){
6761             var k = this.keyCode;
6762             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6763             (k == 16) || (k == 17) ||
6764             (k >= 18 && k <= 20) ||
6765             (k >= 33 && k <= 35) ||
6766             (k >= 36 && k <= 39) ||
6767             (k >= 44 && k <= 45);
6768         },
6769         /**
6770          * Cancels bubbling of the event.
6771          */
6772         stopPropagation : function(){
6773             if(this.browserEvent){
6774                 if(this.type == 'mousedown'){
6775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6776                 }
6777                 E.stopPropagation(this.browserEvent);
6778             }
6779         },
6780
6781         /**
6782          * Gets the key code for the event.
6783          * @return {Number}
6784          */
6785         getCharCode : function(){
6786             return this.charCode || this.keyCode;
6787         },
6788
6789         /**
6790          * Returns a normalized keyCode for the event.
6791          * @return {Number} The key code
6792          */
6793         getKey : function(){
6794             var k = this.keyCode || this.charCode;
6795             return Roo.isSafari ? (safariKeys[k] || k) : k;
6796         },
6797
6798         /**
6799          * Gets the x coordinate of the event.
6800          * @return {Number}
6801          */
6802         getPageX : function(){
6803             return this.xy[0];
6804         },
6805
6806         /**
6807          * Gets the y coordinate of the event.
6808          * @return {Number}
6809          */
6810         getPageY : function(){
6811             return this.xy[1];
6812         },
6813
6814         /**
6815          * Gets the time of the event.
6816          * @return {Number}
6817          */
6818         getTime : function(){
6819             if(this.browserEvent){
6820                 return E.getTime(this.browserEvent);
6821             }
6822             return null;
6823         },
6824
6825         /**
6826          * Gets the page coordinates of the event.
6827          * @return {Array} The xy values like [x, y]
6828          */
6829         getXY : function(){
6830             return this.xy;
6831         },
6832
6833         /**
6834          * Gets the target for the event.
6835          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6836          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6837                 search as a number or element (defaults to 10 || document.body)
6838          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6839          * @return {HTMLelement}
6840          */
6841         getTarget : function(selector, maxDepth, returnEl){
6842             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6843         },
6844         /**
6845          * Gets the related target.
6846          * @return {HTMLElement}
6847          */
6848         getRelatedTarget : function(){
6849             if(this.browserEvent){
6850                 return E.getRelatedTarget(this.browserEvent);
6851             }
6852             return null;
6853         },
6854
6855         /**
6856          * Normalizes mouse wheel delta across browsers
6857          * @return {Number} The delta
6858          */
6859         getWheelDelta : function(){
6860             var e = this.browserEvent;
6861             var delta = 0;
6862             if(e.wheelDelta){ /* IE/Opera. */
6863                 delta = e.wheelDelta/120;
6864             }else if(e.detail){ /* Mozilla case. */
6865                 delta = -e.detail/3;
6866             }
6867             return delta;
6868         },
6869
6870         /**
6871          * Returns true if the control, meta, shift or alt key was pressed during this event.
6872          * @return {Boolean}
6873          */
6874         hasModifier : function(){
6875             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6876         },
6877
6878         /**
6879          * Returns true if the target of this event equals el or is a child of el
6880          * @param {String/HTMLElement/Element} el
6881          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6882          * @return {Boolean}
6883          */
6884         within : function(el, related){
6885             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6886             return t && Roo.fly(el).contains(t);
6887         },
6888
6889         getPoint : function(){
6890             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6891         }
6892     };
6893
6894     return new Roo.EventObjectImpl();
6895 }();
6896             
6897     /*
6898  * Based on:
6899  * Ext JS Library 1.1.1
6900  * Copyright(c) 2006-2007, Ext JS, LLC.
6901  *
6902  * Originally Released Under LGPL - original licence link has changed is not relivant.
6903  *
6904  * Fork - LGPL
6905  * <script type="text/javascript">
6906  */
6907
6908  
6909 // was in Composite Element!??!?!
6910  
6911 (function(){
6912     var D = Roo.lib.Dom;
6913     var E = Roo.lib.Event;
6914     var A = Roo.lib.Anim;
6915
6916     // local style camelizing for speed
6917     var propCache = {};
6918     var camelRe = /(-[a-z])/gi;
6919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6920     var view = document.defaultView;
6921
6922 /**
6923  * @class Roo.Element
6924  * Represents an Element in the DOM.<br><br>
6925  * Usage:<br>
6926 <pre><code>
6927 var el = Roo.get("my-div");
6928
6929 // or with getEl
6930 var el = getEl("my-div");
6931
6932 // or with a DOM element
6933 var el = Roo.get(myDivElement);
6934 </code></pre>
6935  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6936  * each call instead of constructing a new one.<br><br>
6937  * <b>Animations</b><br />
6938  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6939  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6940 <pre>
6941 Option    Default   Description
6942 --------- --------  ---------------------------------------------
6943 duration  .35       The duration of the animation in seconds
6944 easing    easeOut   The YUI easing method
6945 callback  none      A function to execute when the anim completes
6946 scope     this      The scope (this) of the callback function
6947 </pre>
6948 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6949 * manipulate the animation. Here's an example:
6950 <pre><code>
6951 var el = Roo.get("my-div");
6952
6953 // no animation
6954 el.setWidth(100);
6955
6956 // default animation
6957 el.setWidth(100, true);
6958
6959 // animation with some options set
6960 el.setWidth(100, {
6961     duration: 1,
6962     callback: this.foo,
6963     scope: this
6964 });
6965
6966 // using the "anim" property to get the Anim object
6967 var opt = {
6968     duration: 1,
6969     callback: this.foo,
6970     scope: this
6971 };
6972 el.setWidth(100, opt);
6973 ...
6974 if(opt.anim.isAnimated()){
6975     opt.anim.stop();
6976 }
6977 </code></pre>
6978 * <b> Composite (Collections of) Elements</b><br />
6979  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6980  * @constructor Create a new Element directly.
6981  * @param {String/HTMLElement} element
6982  * @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).
6983  */
6984     Roo.Element = function(element, forceNew){
6985         var dom = typeof element == "string" ?
6986                 document.getElementById(element) : element;
6987         if(!dom){ // invalid id/element
6988             return null;
6989         }
6990         var id = dom.id;
6991         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6992             return Roo.Element.cache[id];
6993         }
6994
6995         /**
6996          * The DOM element
6997          * @type HTMLElement
6998          */
6999         this.dom = dom;
7000
7001         /**
7002          * The DOM element ID
7003          * @type String
7004          */
7005         this.id = id || Roo.id(dom);
7006     };
7007
7008     var El = Roo.Element;
7009
7010     El.prototype = {
7011         /**
7012          * The element's default display mode  (defaults to "")
7013          * @type String
7014          */
7015         originalDisplay : "",
7016
7017         visibilityMode : 1,
7018         /**
7019          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7020          * @type String
7021          */
7022         defaultUnit : "px",
7023         /**
7024          * Sets the element's visibility mode. When setVisible() is called it
7025          * will use this to determine whether to set the visibility or the display property.
7026          * @param visMode Element.VISIBILITY or Element.DISPLAY
7027          * @return {Roo.Element} this
7028          */
7029         setVisibilityMode : function(visMode){
7030             this.visibilityMode = visMode;
7031             return this;
7032         },
7033         /**
7034          * Convenience method for setVisibilityMode(Element.DISPLAY)
7035          * @param {String} display (optional) What to set display to when visible
7036          * @return {Roo.Element} this
7037          */
7038         enableDisplayMode : function(display){
7039             this.setVisibilityMode(El.DISPLAY);
7040             if(typeof display != "undefined") this.originalDisplay = display;
7041             return this;
7042         },
7043
7044         /**
7045          * 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)
7046          * @param {String} selector The simple selector to test
7047          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7048                 search as a number or element (defaults to 10 || document.body)
7049          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7050          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7051          */
7052         findParent : function(simpleSelector, maxDepth, returnEl){
7053             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7054             maxDepth = maxDepth || 50;
7055             if(typeof maxDepth != "number"){
7056                 stopEl = Roo.getDom(maxDepth);
7057                 maxDepth = 10;
7058             }
7059             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7060                 if(dq.is(p, simpleSelector)){
7061                     return returnEl ? Roo.get(p) : p;
7062                 }
7063                 depth++;
7064                 p = p.parentNode;
7065             }
7066             return null;
7067         },
7068
7069
7070         /**
7071          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7072          * @param {String} selector The simple selector to test
7073          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7074                 search as a number or element (defaults to 10 || document.body)
7075          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7076          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7077          */
7078         findParentNode : function(simpleSelector, maxDepth, returnEl){
7079             var p = Roo.fly(this.dom.parentNode, '_internal');
7080             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7081         },
7082
7083         /**
7084          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7085          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7086          * @param {String} selector The simple selector to test
7087          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7088                 search as a number or element (defaults to 10 || document.body)
7089          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7090          */
7091         up : function(simpleSelector, maxDepth){
7092             return this.findParentNode(simpleSelector, maxDepth, true);
7093         },
7094
7095
7096
7097         /**
7098          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7099          * @param {String} selector The simple selector to test
7100          * @return {Boolean} True if this element matches the selector, else false
7101          */
7102         is : function(simpleSelector){
7103             return Roo.DomQuery.is(this.dom, simpleSelector);
7104         },
7105
7106         /**
7107          * Perform animation on this element.
7108          * @param {Object} args The YUI animation control args
7109          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7110          * @param {Function} onComplete (optional) Function to call when animation completes
7111          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7112          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7113          * @return {Roo.Element} this
7114          */
7115         animate : function(args, duration, onComplete, easing, animType){
7116             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7117             return this;
7118         },
7119
7120         /*
7121          * @private Internal animation call
7122          */
7123         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7124             animType = animType || 'run';
7125             opt = opt || {};
7126             var anim = Roo.lib.Anim[animType](
7127                 this.dom, args,
7128                 (opt.duration || defaultDur) || .35,
7129                 (opt.easing || defaultEase) || 'easeOut',
7130                 function(){
7131                     Roo.callback(cb, this);
7132                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7133                 },
7134                 this
7135             );
7136             opt.anim = anim;
7137             return anim;
7138         },
7139
7140         // private legacy anim prep
7141         preanim : function(a, i){
7142             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7143         },
7144
7145         /**
7146          * Removes worthless text nodes
7147          * @param {Boolean} forceReclean (optional) By default the element
7148          * keeps track if it has been cleaned already so
7149          * you can call this over and over. However, if you update the element and
7150          * need to force a reclean, you can pass true.
7151          */
7152         clean : function(forceReclean){
7153             if(this.isCleaned && forceReclean !== true){
7154                 return this;
7155             }
7156             var ns = /\S/;
7157             var d = this.dom, n = d.firstChild, ni = -1;
7158             while(n){
7159                 var nx = n.nextSibling;
7160                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7161                     d.removeChild(n);
7162                 }else{
7163                     n.nodeIndex = ++ni;
7164                 }
7165                 n = nx;
7166             }
7167             this.isCleaned = true;
7168             return this;
7169         },
7170
7171         // private
7172         calcOffsetsTo : function(el){
7173             el = Roo.get(el);
7174             var d = el.dom;
7175             var restorePos = false;
7176             if(el.getStyle('position') == 'static'){
7177                 el.position('relative');
7178                 restorePos = true;
7179             }
7180             var x = 0, y =0;
7181             var op = this.dom;
7182             while(op && op != d && op.tagName != 'HTML'){
7183                 x+= op.offsetLeft;
7184                 y+= op.offsetTop;
7185                 op = op.offsetParent;
7186             }
7187             if(restorePos){
7188                 el.position('static');
7189             }
7190             return [x, y];
7191         },
7192
7193         /**
7194          * Scrolls this element into view within the passed container.
7195          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7196          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7197          * @return {Roo.Element} this
7198          */
7199         scrollIntoView : function(container, hscroll){
7200             var c = Roo.getDom(container) || document.body;
7201             var el = this.dom;
7202
7203             var o = this.calcOffsetsTo(c),
7204                 l = o[0],
7205                 t = o[1],
7206                 b = t+el.offsetHeight,
7207                 r = l+el.offsetWidth;
7208
7209             var ch = c.clientHeight;
7210             var ct = parseInt(c.scrollTop, 10);
7211             var cl = parseInt(c.scrollLeft, 10);
7212             var cb = ct + ch;
7213             var cr = cl + c.clientWidth;
7214
7215             if(t < ct){
7216                 c.scrollTop = t;
7217             }else if(b > cb){
7218                 c.scrollTop = b-ch;
7219             }
7220
7221             if(hscroll !== false){
7222                 if(l < cl){
7223                     c.scrollLeft = l;
7224                 }else if(r > cr){
7225                     c.scrollLeft = r-c.clientWidth;
7226                 }
7227             }
7228             return this;
7229         },
7230
7231         // private
7232         scrollChildIntoView : function(child, hscroll){
7233             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7234         },
7235
7236         /**
7237          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7238          * the new height may not be available immediately.
7239          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7240          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7241          * @param {Function} onComplete (optional) Function to call when animation completes
7242          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7243          * @return {Roo.Element} this
7244          */
7245         autoHeight : function(animate, duration, onComplete, easing){
7246             var oldHeight = this.getHeight();
7247             this.clip();
7248             this.setHeight(1); // force clipping
7249             setTimeout(function(){
7250                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7251                 if(!animate){
7252                     this.setHeight(height);
7253                     this.unclip();
7254                     if(typeof onComplete == "function"){
7255                         onComplete();
7256                     }
7257                 }else{
7258                     this.setHeight(oldHeight); // restore original height
7259                     this.setHeight(height, animate, duration, function(){
7260                         this.unclip();
7261                         if(typeof onComplete == "function") onComplete();
7262                     }.createDelegate(this), easing);
7263                 }
7264             }.createDelegate(this), 0);
7265             return this;
7266         },
7267
7268         /**
7269          * Returns true if this element is an ancestor of the passed element
7270          * @param {HTMLElement/String} el The element to check
7271          * @return {Boolean} True if this element is an ancestor of el, else false
7272          */
7273         contains : function(el){
7274             if(!el){return false;}
7275             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7276         },
7277
7278         /**
7279          * Checks whether the element is currently visible using both visibility and display properties.
7280          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7281          * @return {Boolean} True if the element is currently visible, else false
7282          */
7283         isVisible : function(deep) {
7284             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7285             if(deep !== true || !vis){
7286                 return vis;
7287             }
7288             var p = this.dom.parentNode;
7289             while(p && p.tagName.toLowerCase() != "body"){
7290                 if(!Roo.fly(p, '_isVisible').isVisible()){
7291                     return false;
7292                 }
7293                 p = p.parentNode;
7294             }
7295             return true;
7296         },
7297
7298         /**
7299          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7300          * @param {String} selector The CSS selector
7301          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7302          * @return {CompositeElement/CompositeElementLite} The composite element
7303          */
7304         select : function(selector, unique){
7305             return El.select(selector, unique, this.dom);
7306         },
7307
7308         /**
7309          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7310          * @param {String} selector The CSS selector
7311          * @return {Array} An array of the matched nodes
7312          */
7313         query : function(selector, unique){
7314             return Roo.DomQuery.select(selector, this.dom);
7315         },
7316
7317         /**
7318          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7319          * @param {String} selector The CSS selector
7320          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7321          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7322          */
7323         child : function(selector, returnDom){
7324             var n = Roo.DomQuery.selectNode(selector, this.dom);
7325             return returnDom ? n : Roo.get(n);
7326         },
7327
7328         /**
7329          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7330          * @param {String} selector The CSS selector
7331          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7332          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7333          */
7334         down : function(selector, returnDom){
7335             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7336             return returnDom ? n : Roo.get(n);
7337         },
7338
7339         /**
7340          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7341          * @param {String} group The group the DD object is member of
7342          * @param {Object} config The DD config object
7343          * @param {Object} overrides An object containing methods to override/implement on the DD object
7344          * @return {Roo.dd.DD} The DD object
7345          */
7346         initDD : function(group, config, overrides){
7347             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7348             return Roo.apply(dd, overrides);
7349         },
7350
7351         /**
7352          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7353          * @param {String} group The group the DDProxy object is member of
7354          * @param {Object} config The DDProxy config object
7355          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7356          * @return {Roo.dd.DDProxy} The DDProxy object
7357          */
7358         initDDProxy : function(group, config, overrides){
7359             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7360             return Roo.apply(dd, overrides);
7361         },
7362
7363         /**
7364          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7365          * @param {String} group The group the DDTarget object is member of
7366          * @param {Object} config The DDTarget config object
7367          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7368          * @return {Roo.dd.DDTarget} The DDTarget object
7369          */
7370         initDDTarget : function(group, config, overrides){
7371             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7372             return Roo.apply(dd, overrides);
7373         },
7374
7375         /**
7376          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7377          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7378          * @param {Boolean} visible Whether the element is visible
7379          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7380          * @return {Roo.Element} this
7381          */
7382          setVisible : function(visible, animate){
7383             if(!animate || !A){
7384                 if(this.visibilityMode == El.DISPLAY){
7385                     this.setDisplayed(visible);
7386                 }else{
7387                     this.fixDisplay();
7388                     this.dom.style.visibility = visible ? "visible" : "hidden";
7389                 }
7390             }else{
7391                 // closure for composites
7392                 var dom = this.dom;
7393                 var visMode = this.visibilityMode;
7394                 if(visible){
7395                     this.setOpacity(.01);
7396                     this.setVisible(true);
7397                 }
7398                 this.anim({opacity: { to: (visible?1:0) }},
7399                       this.preanim(arguments, 1),
7400                       null, .35, 'easeIn', function(){
7401                          if(!visible){
7402                              if(visMode == El.DISPLAY){
7403                                  dom.style.display = "none";
7404                              }else{
7405                                  dom.style.visibility = "hidden";
7406                              }
7407                              Roo.get(dom).setOpacity(1);
7408                          }
7409                      });
7410             }
7411             return this;
7412         },
7413
7414         /**
7415          * Returns true if display is not "none"
7416          * @return {Boolean}
7417          */
7418         isDisplayed : function() {
7419             return this.getStyle("display") != "none";
7420         },
7421
7422         /**
7423          * Toggles the element's visibility or display, depending on visibility mode.
7424          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7425          * @return {Roo.Element} this
7426          */
7427         toggle : function(animate){
7428             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7429             return this;
7430         },
7431
7432         /**
7433          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7434          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7435          * @return {Roo.Element} this
7436          */
7437         setDisplayed : function(value) {
7438             if(typeof value == "boolean"){
7439                value = value ? this.originalDisplay : "none";
7440             }
7441             this.setStyle("display", value);
7442             return this;
7443         },
7444
7445         /**
7446          * Tries to focus the element. Any exceptions are caught and ignored.
7447          * @return {Roo.Element} this
7448          */
7449         focus : function() {
7450             try{
7451                 this.dom.focus();
7452             }catch(e){}
7453             return this;
7454         },
7455
7456         /**
7457          * Tries to blur the element. Any exceptions are caught and ignored.
7458          * @return {Roo.Element} this
7459          */
7460         blur : function() {
7461             try{
7462                 this.dom.blur();
7463             }catch(e){}
7464             return this;
7465         },
7466
7467         /**
7468          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7469          * @param {String/Array} className The CSS class to add, or an array of classes
7470          * @return {Roo.Element} this
7471          */
7472         addClass : function(className){
7473             if(className instanceof Array){
7474                 for(var i = 0, len = className.length; i < len; i++) {
7475                     this.addClass(className[i]);
7476                 }
7477             }else{
7478                 if(className && !this.hasClass(className)){
7479                     this.dom.className = this.dom.className + " " + className;
7480                 }
7481             }
7482             return this;
7483         },
7484
7485         /**
7486          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7487          * @param {String/Array} className The CSS class to add, or an array of classes
7488          * @return {Roo.Element} this
7489          */
7490         radioClass : function(className){
7491             var siblings = this.dom.parentNode.childNodes;
7492             for(var i = 0; i < siblings.length; i++) {
7493                 var s = siblings[i];
7494                 if(s.nodeType == 1){
7495                     Roo.get(s).removeClass(className);
7496                 }
7497             }
7498             this.addClass(className);
7499             return this;
7500         },
7501
7502         /**
7503          * Removes one or more CSS classes from the element.
7504          * @param {String/Array} className The CSS class to remove, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         removeClass : function(className){
7508             if(!className || !this.dom.className){
7509                 return this;
7510             }
7511             if(className instanceof Array){
7512                 for(var i = 0, len = className.length; i < len; i++) {
7513                     this.removeClass(className[i]);
7514                 }
7515             }else{
7516                 if(this.hasClass(className)){
7517                     var re = this.classReCache[className];
7518                     if (!re) {
7519                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7520                        this.classReCache[className] = re;
7521                     }
7522                     this.dom.className =
7523                         this.dom.className.replace(re, " ");
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         // private
7530         classReCache: {},
7531
7532         /**
7533          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7534          * @param {String} className The CSS class to toggle
7535          * @return {Roo.Element} this
7536          */
7537         toggleClass : function(className){
7538             if(this.hasClass(className)){
7539                 this.removeClass(className);
7540             }else{
7541                 this.addClass(className);
7542             }
7543             return this;
7544         },
7545
7546         /**
7547          * Checks if the specified CSS class exists on this element's DOM node.
7548          * @param {String} className The CSS class to check for
7549          * @return {Boolean} True if the class exists, else false
7550          */
7551         hasClass : function(className){
7552             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7553         },
7554
7555         /**
7556          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7557          * @param {String} oldClassName The CSS class to replace
7558          * @param {String} newClassName The replacement CSS class
7559          * @return {Roo.Element} this
7560          */
7561         replaceClass : function(oldClassName, newClassName){
7562             this.removeClass(oldClassName);
7563             this.addClass(newClassName);
7564             return this;
7565         },
7566
7567         /**
7568          * Returns an object with properties matching the styles requested.
7569          * For example, el.getStyles('color', 'font-size', 'width') might return
7570          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7571          * @param {String} style1 A style name
7572          * @param {String} style2 A style name
7573          * @param {String} etc.
7574          * @return {Object} The style object
7575          */
7576         getStyles : function(){
7577             var a = arguments, len = a.length, r = {};
7578             for(var i = 0; i < len; i++){
7579                 r[a[i]] = this.getStyle(a[i]);
7580             }
7581             return r;
7582         },
7583
7584         /**
7585          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7586          * @param {String} property The style property whose value is returned.
7587          * @return {String} The current value of the style property for this element.
7588          */
7589         getStyle : function(){
7590             return view && view.getComputedStyle ?
7591                 function(prop){
7592                     var el = this.dom, v, cs, camel;
7593                     if(prop == 'float'){
7594                         prop = "cssFloat";
7595                     }
7596                     if(el.style && (v = el.style[prop])){
7597                         return v;
7598                     }
7599                     if(cs = view.getComputedStyle(el, "")){
7600                         if(!(camel = propCache[prop])){
7601                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7602                         }
7603                         return cs[camel];
7604                     }
7605                     return null;
7606                 } :
7607                 function(prop){
7608                     var el = this.dom, v, cs, camel;
7609                     if(prop == 'opacity'){
7610                         if(typeof el.style.filter == 'string'){
7611                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7612                             if(m){
7613                                 var fv = parseFloat(m[1]);
7614                                 if(!isNaN(fv)){
7615                                     return fv ? fv / 100 : 0;
7616                                 }
7617                             }
7618                         }
7619                         return 1;
7620                     }else if(prop == 'float'){
7621                         prop = "styleFloat";
7622                     }
7623                     if(!(camel = propCache[prop])){
7624                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7625                     }
7626                     if(v = el.style[camel]){
7627                         return v;
7628                     }
7629                     if(cs = el.currentStyle){
7630                         return cs[camel];
7631                     }
7632                     return null;
7633                 };
7634         }(),
7635
7636         /**
7637          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7638          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7639          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7640          * @return {Roo.Element} this
7641          */
7642         setStyle : function(prop, value){
7643             if(typeof prop == "string"){
7644                 
7645                 if (prop == 'float') {
7646                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7647                     return this;
7648                 }
7649                 
7650                 var camel;
7651                 if(!(camel = propCache[prop])){
7652                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7653                 }
7654                 
7655                 if(camel == 'opacity') {
7656                     this.setOpacity(value);
7657                 }else{
7658                     this.dom.style[camel] = value;
7659                 }
7660             }else{
7661                 for(var style in prop){
7662                     if(typeof prop[style] != "function"){
7663                        this.setStyle(style, prop[style]);
7664                     }
7665                 }
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * More flexible version of {@link #setStyle} for setting style properties.
7672          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7673          * a function which returns such a specification.
7674          * @return {Roo.Element} this
7675          */
7676         applyStyles : function(style){
7677             Roo.DomHelper.applyStyles(this.dom, style);
7678             return this;
7679         },
7680
7681         /**
7682           * 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).
7683           * @return {Number} The X position of the element
7684           */
7685         getX : function(){
7686             return D.getX(this.dom);
7687         },
7688
7689         /**
7690           * 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).
7691           * @return {Number} The Y position of the element
7692           */
7693         getY : function(){
7694             return D.getY(this.dom);
7695         },
7696
7697         /**
7698           * 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).
7699           * @return {Array} The XY position of the element
7700           */
7701         getXY : function(){
7702             return D.getXY(this.dom);
7703         },
7704
7705         /**
7706          * 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).
7707          * @param {Number} The X position of the element
7708          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7709          * @return {Roo.Element} this
7710          */
7711         setX : function(x, animate){
7712             if(!animate || !A){
7713                 D.setX(this.dom, x);
7714             }else{
7715                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7716             }
7717             return this;
7718         },
7719
7720         /**
7721          * 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).
7722          * @param {Number} The Y position of the element
7723          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7724          * @return {Roo.Element} this
7725          */
7726         setY : function(y, animate){
7727             if(!animate || !A){
7728                 D.setY(this.dom, y);
7729             }else{
7730                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7731             }
7732             return this;
7733         },
7734
7735         /**
7736          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7737          * @param {String} left The left CSS property value
7738          * @return {Roo.Element} this
7739          */
7740         setLeft : function(left){
7741             this.setStyle("left", this.addUnits(left));
7742             return this;
7743         },
7744
7745         /**
7746          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7747          * @param {String} top The top CSS property value
7748          * @return {Roo.Element} this
7749          */
7750         setTop : function(top){
7751             this.setStyle("top", this.addUnits(top));
7752             return this;
7753         },
7754
7755         /**
7756          * Sets the element's CSS right style.
7757          * @param {String} right The right CSS property value
7758          * @return {Roo.Element} this
7759          */
7760         setRight : function(right){
7761             this.setStyle("right", this.addUnits(right));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the element's CSS bottom style.
7767          * @param {String} bottom The bottom CSS property value
7768          * @return {Roo.Element} this
7769          */
7770         setBottom : function(bottom){
7771             this.setStyle("bottom", this.addUnits(bottom));
7772             return this;
7773         },
7774
7775         /**
7776          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7777          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7778          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7779          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7780          * @return {Roo.Element} this
7781          */
7782         setXY : function(pos, animate){
7783             if(!animate || !A){
7784                 D.setXY(this.dom, pos);
7785             }else{
7786                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7787             }
7788             return this;
7789         },
7790
7791         /**
7792          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7793          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7794          * @param {Number} x X value for new position (coordinates are page-based)
7795          * @param {Number} y Y value for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setLocation : function(x, y, animate){
7800             this.setXY([x, y], this.preanim(arguments, 2));
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7806          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7807          * @param {Number} x X value for new position (coordinates are page-based)
7808          * @param {Number} y Y value for new position (coordinates are page-based)
7809          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7810          * @return {Roo.Element} this
7811          */
7812         moveTo : function(x, y, animate){
7813             this.setXY([x, y], this.preanim(arguments, 2));
7814             return this;
7815         },
7816
7817         /**
7818          * Returns the region of the given element.
7819          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7820          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7821          */
7822         getRegion : function(){
7823             return D.getRegion(this.dom);
7824         },
7825
7826         /**
7827          * Returns the offset height of the element
7828          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7829          * @return {Number} The element's height
7830          */
7831         getHeight : function(contentHeight){
7832             var h = this.dom.offsetHeight || 0;
7833             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7834         },
7835
7836         /**
7837          * Returns the offset width of the element
7838          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7839          * @return {Number} The element's width
7840          */
7841         getWidth : function(contentWidth){
7842             var w = this.dom.offsetWidth || 0;
7843             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7844         },
7845
7846         /**
7847          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7848          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7849          * if a height has not been set using CSS.
7850          * @return {Number}
7851          */
7852         getComputedHeight : function(){
7853             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7854             if(!h){
7855                 h = parseInt(this.getStyle('height'), 10) || 0;
7856                 if(!this.isBorderBox()){
7857                     h += this.getFrameWidth('tb');
7858                 }
7859             }
7860             return h;
7861         },
7862
7863         /**
7864          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7865          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7866          * if a width has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedWidth : function(){
7870             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7871             if(!w){
7872                 w = parseInt(this.getStyle('width'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     w += this.getFrameWidth('lr');
7875                 }
7876             }
7877             return w;
7878         },
7879
7880         /**
7881          * Returns the size of the element.
7882          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7883          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7884          */
7885         getSize : function(contentSize){
7886             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7887         },
7888
7889         /**
7890          * Returns the width and height of the viewport.
7891          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7892          */
7893         getViewSize : function(){
7894             var d = this.dom, doc = document, aw = 0, ah = 0;
7895             if(d == doc || d == doc.body){
7896                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7897             }else{
7898                 return {
7899                     width : d.clientWidth,
7900                     height: d.clientHeight
7901                 };
7902             }
7903         },
7904
7905         /**
7906          * Returns the value of the "value" attribute
7907          * @param {Boolean} asNumber true to parse the value as a number
7908          * @return {String/Number}
7909          */
7910         getValue : function(asNumber){
7911             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7912         },
7913
7914         // private
7915         adjustWidth : function(width){
7916             if(typeof width == "number"){
7917                 if(this.autoBoxAdjust && !this.isBorderBox()){
7918                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7919                 }
7920                 if(width < 0){
7921                     width = 0;
7922                 }
7923             }
7924             return width;
7925         },
7926
7927         // private
7928         adjustHeight : function(height){
7929             if(typeof height == "number"){
7930                if(this.autoBoxAdjust && !this.isBorderBox()){
7931                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7932                }
7933                if(height < 0){
7934                    height = 0;
7935                }
7936             }
7937             return height;
7938         },
7939
7940         /**
7941          * Set the width of the element
7942          * @param {Number} width The new width
7943          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7944          * @return {Roo.Element} this
7945          */
7946         setWidth : function(width, animate){
7947             width = this.adjustWidth(width);
7948             if(!animate || !A){
7949                 this.dom.style.width = this.addUnits(width);
7950             }else{
7951                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7952             }
7953             return this;
7954         },
7955
7956         /**
7957          * Set the height of the element
7958          * @param {Number} height The new height
7959          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962          setHeight : function(height, animate){
7963             height = this.adjustHeight(height);
7964             if(!animate || !A){
7965                 this.dom.style.height = this.addUnits(height);
7966             }else{
7967                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7968             }
7969             return this;
7970         },
7971
7972         /**
7973          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7974          * @param {Number} width The new width
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setSize : function(width, height, animate){
7980             if(typeof width == "object"){ // in case of object from getSize()
7981                 height = width.height; width = width.width;
7982             }
7983             width = this.adjustWidth(width); height = this.adjustHeight(height);
7984             if(!animate || !A){
7985                 this.dom.style.width = this.addUnits(width);
7986                 this.dom.style.height = this.addUnits(height);
7987             }else{
7988                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7995          * @param {Number} x X value for new position (coordinates are page-based)
7996          * @param {Number} y Y value for new position (coordinates are page-based)
7997          * @param {Number} width The new width
7998          * @param {Number} height The new height
7999          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8000          * @return {Roo.Element} this
8001          */
8002         setBounds : function(x, y, width, height, animate){
8003             if(!animate || !A){
8004                 this.setSize(width, height);
8005                 this.setLocation(x, y);
8006             }else{
8007                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8008                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8009                               this.preanim(arguments, 4), 'motion');
8010             }
8011             return this;
8012         },
8013
8014         /**
8015          * 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.
8016          * @param {Roo.lib.Region} region The region to fill
8017          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8018          * @return {Roo.Element} this
8019          */
8020         setRegion : function(region, animate){
8021             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8022             return this;
8023         },
8024
8025         /**
8026          * Appends an event handler
8027          *
8028          * @param {String}   eventName     The type of event to append
8029          * @param {Function} fn        The method the event invokes
8030          * @param {Object} scope       (optional) The scope (this object) of the fn
8031          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8032          */
8033         addListener : function(eventName, fn, scope, options){
8034             if (this.dom) {
8035                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8036             }
8037         },
8038
8039         /**
8040          * Removes an event handler from this element
8041          * @param {String} eventName the type of event to remove
8042          * @param {Function} fn the method the event invokes
8043          * @return {Roo.Element} this
8044          */
8045         removeListener : function(eventName, fn){
8046             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8047             return this;
8048         },
8049
8050         /**
8051          * Removes all previous added listeners from this element
8052          * @return {Roo.Element} this
8053          */
8054         removeAllListeners : function(){
8055             E.purgeElement(this.dom);
8056             return this;
8057         },
8058
8059         relayEvent : function(eventName, observable){
8060             this.on(eventName, function(e){
8061                 observable.fireEvent(eventName, e);
8062             });
8063         },
8064
8065         /**
8066          * Set the opacity of the element
8067          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8068          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8069          * @return {Roo.Element} this
8070          */
8071          setOpacity : function(opacity, animate){
8072             if(!animate || !A){
8073                 var s = this.dom.style;
8074                 if(Roo.isIE){
8075                     s.zoom = 1;
8076                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8077                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8078                 }else{
8079                     s.opacity = opacity;
8080                 }
8081             }else{
8082                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8083             }
8084             return this;
8085         },
8086
8087         /**
8088          * Gets the left X coordinate
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getLeft : function(local){
8093             if(!local){
8094                 return this.getX();
8095             }else{
8096                 return parseInt(this.getStyle("left"), 10) || 0;
8097             }
8098         },
8099
8100         /**
8101          * Gets the right X coordinate of the element (element X position + element width)
8102          * @param {Boolean} local True to get the local css position instead of page coordinate
8103          * @return {Number}
8104          */
8105         getRight : function(local){
8106             if(!local){
8107                 return this.getX() + this.getWidth();
8108             }else{
8109                 return (this.getLeft(true) + this.getWidth()) || 0;
8110             }
8111         },
8112
8113         /**
8114          * Gets the top Y coordinate
8115          * @param {Boolean} local True to get the local css position instead of page coordinate
8116          * @return {Number}
8117          */
8118         getTop : function(local) {
8119             if(!local){
8120                 return this.getY();
8121             }else{
8122                 return parseInt(this.getStyle("top"), 10) || 0;
8123             }
8124         },
8125
8126         /**
8127          * Gets the bottom Y coordinate of the element (element Y position + element height)
8128          * @param {Boolean} local True to get the local css position instead of page coordinate
8129          * @return {Number}
8130          */
8131         getBottom : function(local){
8132             if(!local){
8133                 return this.getY() + this.getHeight();
8134             }else{
8135                 return (this.getTop(true) + this.getHeight()) || 0;
8136             }
8137         },
8138
8139         /**
8140         * Initializes positioning on this element. If a desired position is not passed, it will make the
8141         * the element positioned relative IF it is not already positioned.
8142         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8143         * @param {Number} zIndex (optional) The zIndex to apply
8144         * @param {Number} x (optional) Set the page X position
8145         * @param {Number} y (optional) Set the page Y position
8146         */
8147         position : function(pos, zIndex, x, y){
8148             if(!pos){
8149                if(this.getStyle('position') == 'static'){
8150                    this.setStyle('position', 'relative');
8151                }
8152             }else{
8153                 this.setStyle("position", pos);
8154             }
8155             if(zIndex){
8156                 this.setStyle("z-index", zIndex);
8157             }
8158             if(x !== undefined && y !== undefined){
8159                 this.setXY([x, y]);
8160             }else if(x !== undefined){
8161                 this.setX(x);
8162             }else if(y !== undefined){
8163                 this.setY(y);
8164             }
8165         },
8166
8167         /**
8168         * Clear positioning back to the default when the document was loaded
8169         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8170         * @return {Roo.Element} this
8171          */
8172         clearPositioning : function(value){
8173             value = value ||'';
8174             this.setStyle({
8175                 "left": value,
8176                 "right": value,
8177                 "top": value,
8178                 "bottom": value,
8179                 "z-index": "",
8180                 "position" : "static"
8181             });
8182             return this;
8183         },
8184
8185         /**
8186         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8187         * snapshot before performing an update and then restoring the element.
8188         * @return {Object}
8189         */
8190         getPositioning : function(){
8191             var l = this.getStyle("left");
8192             var t = this.getStyle("top");
8193             return {
8194                 "position" : this.getStyle("position"),
8195                 "left" : l,
8196                 "right" : l ? "" : this.getStyle("right"),
8197                 "top" : t,
8198                 "bottom" : t ? "" : this.getStyle("bottom"),
8199                 "z-index" : this.getStyle("z-index")
8200             };
8201         },
8202
8203         /**
8204          * Gets the width of the border(s) for the specified side(s)
8205          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8206          * passing lr would get the border (l)eft width + the border (r)ight width.
8207          * @return {Number} The width of the sides passed added together
8208          */
8209         getBorderWidth : function(side){
8210             return this.addStyles(side, El.borders);
8211         },
8212
8213         /**
8214          * Gets the width of the padding(s) for the specified side(s)
8215          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8216          * passing lr would get the padding (l)eft + the padding (r)ight.
8217          * @return {Number} The padding of the sides passed added together
8218          */
8219         getPadding : function(side){
8220             return this.addStyles(side, El.paddings);
8221         },
8222
8223         /**
8224         * Set positioning with an object returned by getPositioning().
8225         * @param {Object} posCfg
8226         * @return {Roo.Element} this
8227          */
8228         setPositioning : function(pc){
8229             this.applyStyles(pc);
8230             if(pc.right == "auto"){
8231                 this.dom.style.right = "";
8232             }
8233             if(pc.bottom == "auto"){
8234                 this.dom.style.bottom = "";
8235             }
8236             return this;
8237         },
8238
8239         // private
8240         fixDisplay : function(){
8241             if(this.getStyle("display") == "none"){
8242                 this.setStyle("visibility", "hidden");
8243                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8244                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8245                     this.setStyle("display", "block");
8246                 }
8247             }
8248         },
8249
8250         /**
8251          * Quick set left and top adding default units
8252          * @param {String} left The left CSS property value
8253          * @param {String} top The top CSS property value
8254          * @return {Roo.Element} this
8255          */
8256          setLeftTop : function(left, top){
8257             this.dom.style.left = this.addUnits(left);
8258             this.dom.style.top = this.addUnits(top);
8259             return this;
8260         },
8261
8262         /**
8263          * Move this element relative to its current position.
8264          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8265          * @param {Number} distance How far to move the element in pixels
8266          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8267          * @return {Roo.Element} this
8268          */
8269          move : function(direction, distance, animate){
8270             var xy = this.getXY();
8271             direction = direction.toLowerCase();
8272             switch(direction){
8273                 case "l":
8274                 case "left":
8275                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8276                     break;
8277                case "r":
8278                case "right":
8279                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8280                     break;
8281                case "t":
8282                case "top":
8283                case "up":
8284                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8285                     break;
8286                case "b":
8287                case "bottom":
8288                case "down":
8289                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8290                     break;
8291             }
8292             return this;
8293         },
8294
8295         /**
8296          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8297          * @return {Roo.Element} this
8298          */
8299         clip : function(){
8300             if(!this.isClipped){
8301                this.isClipped = true;
8302                this.originalClip = {
8303                    "o": this.getStyle("overflow"),
8304                    "x": this.getStyle("overflow-x"),
8305                    "y": this.getStyle("overflow-y")
8306                };
8307                this.setStyle("overflow", "hidden");
8308                this.setStyle("overflow-x", "hidden");
8309                this.setStyle("overflow-y", "hidden");
8310             }
8311             return this;
8312         },
8313
8314         /**
8315          *  Return clipping (overflow) to original clipping before clip() was called
8316          * @return {Roo.Element} this
8317          */
8318         unclip : function(){
8319             if(this.isClipped){
8320                 this.isClipped = false;
8321                 var o = this.originalClip;
8322                 if(o.o){this.setStyle("overflow", o.o);}
8323                 if(o.x){this.setStyle("overflow-x", o.x);}
8324                 if(o.y){this.setStyle("overflow-y", o.y);}
8325             }
8326             return this;
8327         },
8328
8329
8330         /**
8331          * Gets the x,y coordinates specified by the anchor position on the element.
8332          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8333          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8334          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8335          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8336          * @return {Array} [x, y] An array containing the element's x and y coordinates
8337          */
8338         getAnchorXY : function(anchor, local, s){
8339             //Passing a different size is useful for pre-calculating anchors,
8340             //especially for anchored animations that change the el size.
8341
8342             var w, h, vp = false;
8343             if(!s){
8344                 var d = this.dom;
8345                 if(d == document.body || d == document){
8346                     vp = true;
8347                     w = D.getViewWidth(); h = D.getViewHeight();
8348                 }else{
8349                     w = this.getWidth(); h = this.getHeight();
8350                 }
8351             }else{
8352                 w = s.width;  h = s.height;
8353             }
8354             var x = 0, y = 0, r = Math.round;
8355             switch((anchor || "tl").toLowerCase()){
8356                 case "c":
8357                     x = r(w*.5);
8358                     y = r(h*.5);
8359                 break;
8360                 case "t":
8361                     x = r(w*.5);
8362                     y = 0;
8363                 break;
8364                 case "l":
8365                     x = 0;
8366                     y = r(h*.5);
8367                 break;
8368                 case "r":
8369                     x = w;
8370                     y = r(h*.5);
8371                 break;
8372                 case "b":
8373                     x = r(w*.5);
8374                     y = h;
8375                 break;
8376                 case "tl":
8377                     x = 0;
8378                     y = 0;
8379                 break;
8380                 case "bl":
8381                     x = 0;
8382                     y = h;
8383                 break;
8384                 case "br":
8385                     x = w;
8386                     y = h;
8387                 break;
8388                 case "tr":
8389                     x = w;
8390                     y = 0;
8391                 break;
8392             }
8393             if(local === true){
8394                 return [x, y];
8395             }
8396             if(vp){
8397                 var sc = this.getScroll();
8398                 return [x + sc.left, y + sc.top];
8399             }
8400             //Add the element's offset xy
8401             var o = this.getXY();
8402             return [x+o[0], y+o[1]];
8403         },
8404
8405         /**
8406          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8407          * supported position values.
8408          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8409          * @param {String} position The position to align to.
8410          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8411          * @return {Array} [x, y]
8412          */
8413         getAlignToXY : function(el, p, o){
8414             el = Roo.get(el);
8415             var d = this.dom;
8416             if(!el.dom){
8417                 throw "Element.alignTo with an element that doesn't exist";
8418             }
8419             var c = false; //constrain to viewport
8420             var p1 = "", p2 = "";
8421             o = o || [0,0];
8422
8423             if(!p){
8424                 p = "tl-bl";
8425             }else if(p == "?"){
8426                 p = "tl-bl?";
8427             }else if(p.indexOf("-") == -1){
8428                 p = "tl-" + p;
8429             }
8430             p = p.toLowerCase();
8431             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8432             if(!m){
8433                throw "Element.alignTo with an invalid alignment " + p;
8434             }
8435             p1 = m[1]; p2 = m[2]; c = !!m[3];
8436
8437             //Subtract the aligned el's internal xy from the target's offset xy
8438             //plus custom offset to get the aligned el's new offset xy
8439             var a1 = this.getAnchorXY(p1, true);
8440             var a2 = el.getAnchorXY(p2, false);
8441             var x = a2[0] - a1[0] + o[0];
8442             var y = a2[1] - a1[1] + o[1];
8443             if(c){
8444                 //constrain the aligned el to viewport if necessary
8445                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8446                 // 5px of margin for ie
8447                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8448
8449                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8450                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8451                 //otherwise swap the aligned el to the opposite border of the target.
8452                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8453                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8454                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8455                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8456
8457                var doc = document;
8458                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8459                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8460
8461                if((x+w) > dw + scrollX){
8462                     x = swapX ? r.left-w : dw+scrollX-w;
8463                 }
8464                if(x < scrollX){
8465                    x = swapX ? r.right : scrollX;
8466                }
8467                if((y+h) > dh + scrollY){
8468                     y = swapY ? r.top-h : dh+scrollY-h;
8469                 }
8470                if (y < scrollY){
8471                    y = swapY ? r.bottom : scrollY;
8472                }
8473             }
8474             return [x,y];
8475         },
8476
8477         // private
8478         getConstrainToXY : function(){
8479             var os = {top:0, left:0, bottom:0, right: 0};
8480
8481             return function(el, local, offsets, proposedXY){
8482                 el = Roo.get(el);
8483                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8484
8485                 var vw, vh, vx = 0, vy = 0;
8486                 if(el.dom == document.body || el.dom == document){
8487                     vw = Roo.lib.Dom.getViewWidth();
8488                     vh = Roo.lib.Dom.getViewHeight();
8489                 }else{
8490                     vw = el.dom.clientWidth;
8491                     vh = el.dom.clientHeight;
8492                     if(!local){
8493                         var vxy = el.getXY();
8494                         vx = vxy[0];
8495                         vy = vxy[1];
8496                     }
8497                 }
8498
8499                 var s = el.getScroll();
8500
8501                 vx += offsets.left + s.left;
8502                 vy += offsets.top + s.top;
8503
8504                 vw -= offsets.right;
8505                 vh -= offsets.bottom;
8506
8507                 var vr = vx+vw;
8508                 var vb = vy+vh;
8509
8510                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8511                 var x = xy[0], y = xy[1];
8512                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8513
8514                 // only move it if it needs it
8515                 var moved = false;
8516
8517                 // first validate right/bottom
8518                 if((x + w) > vr){
8519                     x = vr - w;
8520                     moved = true;
8521                 }
8522                 if((y + h) > vb){
8523                     y = vb - h;
8524                     moved = true;
8525                 }
8526                 // then make sure top/left isn't negative
8527                 if(x < vx){
8528                     x = vx;
8529                     moved = true;
8530                 }
8531                 if(y < vy){
8532                     y = vy;
8533                     moved = true;
8534                 }
8535                 return moved ? [x, y] : false;
8536             };
8537         }(),
8538
8539         // private
8540         adjustForConstraints : function(xy, parent, offsets){
8541             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8542         },
8543
8544         /**
8545          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8546          * document it aligns it to the viewport.
8547          * The position parameter is optional, and can be specified in any one of the following formats:
8548          * <ul>
8549          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8550          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8551          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8552          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8553          *   <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
8554          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8555          * </ul>
8556          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8557          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8558          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8559          * that specified in order to enforce the viewport constraints.
8560          * Following are all of the supported anchor positions:
8561     <pre>
8562     Value  Description
8563     -----  -----------------------------
8564     tl     The top left corner (default)
8565     t      The center of the top edge
8566     tr     The top right corner
8567     l      The center of the left edge
8568     c      In the center of the element
8569     r      The center of the right edge
8570     bl     The bottom left corner
8571     b      The center of the bottom edge
8572     br     The bottom right corner
8573     </pre>
8574     Example Usage:
8575     <pre><code>
8576     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8577     el.alignTo("other-el");
8578
8579     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8580     el.alignTo("other-el", "tr?");
8581
8582     // align the bottom right corner of el with the center left edge of other-el
8583     el.alignTo("other-el", "br-l?");
8584
8585     // align the center of el with the bottom left corner of other-el and
8586     // adjust the x position by -6 pixels (and the y position by 0)
8587     el.alignTo("other-el", "c-bl", [-6, 0]);
8588     </code></pre>
8589          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8590          * @param {String} position The position to align to.
8591          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8592          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8593          * @return {Roo.Element} this
8594          */
8595         alignTo : function(element, position, offsets, animate){
8596             var xy = this.getAlignToXY(element, position, offsets);
8597             this.setXY(xy, this.preanim(arguments, 3));
8598             return this;
8599         },
8600
8601         /**
8602          * Anchors an element to another element and realigns it when the window is resized.
8603          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8604          * @param {String} position The position to align to.
8605          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8606          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8607          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8608          * is a number, it is used as the buffer delay (defaults to 50ms).
8609          * @param {Function} callback The function to call after the animation finishes
8610          * @return {Roo.Element} this
8611          */
8612         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8613             var action = function(){
8614                 this.alignTo(el, alignment, offsets, animate);
8615                 Roo.callback(callback, this);
8616             };
8617             Roo.EventManager.onWindowResize(action, this);
8618             var tm = typeof monitorScroll;
8619             if(tm != 'undefined'){
8620                 Roo.EventManager.on(window, 'scroll', action, this,
8621                     {buffer: tm == 'number' ? monitorScroll : 50});
8622             }
8623             action.call(this); // align immediately
8624             return this;
8625         },
8626         /**
8627          * Clears any opacity settings from this element. Required in some cases for IE.
8628          * @return {Roo.Element} this
8629          */
8630         clearOpacity : function(){
8631             if (window.ActiveXObject) {
8632                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8633                     this.dom.style.filter = "";
8634                 }
8635             } else {
8636                 this.dom.style.opacity = "";
8637                 this.dom.style["-moz-opacity"] = "";
8638                 this.dom.style["-khtml-opacity"] = "";
8639             }
8640             return this;
8641         },
8642
8643         /**
8644          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8645          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8646          * @return {Roo.Element} this
8647          */
8648         hide : function(animate){
8649             this.setVisible(false, this.preanim(arguments, 0));
8650             return this;
8651         },
8652
8653         /**
8654         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8655         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8656          * @return {Roo.Element} this
8657          */
8658         show : function(animate){
8659             this.setVisible(true, this.preanim(arguments, 0));
8660             return this;
8661         },
8662
8663         /**
8664          * @private Test if size has a unit, otherwise appends the default
8665          */
8666         addUnits : function(size){
8667             return Roo.Element.addUnits(size, this.defaultUnit);
8668         },
8669
8670         /**
8671          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8672          * @return {Roo.Element} this
8673          */
8674         beginMeasure : function(){
8675             var el = this.dom;
8676             if(el.offsetWidth || el.offsetHeight){
8677                 return this; // offsets work already
8678             }
8679             var changed = [];
8680             var p = this.dom, b = document.body; // start with this element
8681             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8682                 var pe = Roo.get(p);
8683                 if(pe.getStyle('display') == 'none'){
8684                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8685                     p.style.visibility = "hidden";
8686                     p.style.display = "block";
8687                 }
8688                 p = p.parentNode;
8689             }
8690             this._measureChanged = changed;
8691             return this;
8692
8693         },
8694
8695         /**
8696          * Restores displays to before beginMeasure was called
8697          * @return {Roo.Element} this
8698          */
8699         endMeasure : function(){
8700             var changed = this._measureChanged;
8701             if(changed){
8702                 for(var i = 0, len = changed.length; i < len; i++) {
8703                     var r = changed[i];
8704                     r.el.style.visibility = r.visibility;
8705                     r.el.style.display = "none";
8706                 }
8707                 this._measureChanged = null;
8708             }
8709             return this;
8710         },
8711
8712         /**
8713         * Update the innerHTML of this element, optionally searching for and processing scripts
8714         * @param {String} html The new HTML
8715         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8716         * @param {Function} callback For async script loading you can be noticed when the update completes
8717         * @return {Roo.Element} this
8718          */
8719         update : function(html, loadScripts, callback){
8720             if(typeof html == "undefined"){
8721                 html = "";
8722             }
8723             if(loadScripts !== true){
8724                 this.dom.innerHTML = html;
8725                 if(typeof callback == "function"){
8726                     callback();
8727                 }
8728                 return this;
8729             }
8730             var id = Roo.id();
8731             var dom = this.dom;
8732
8733             html += '<span id="' + id + '"></span>';
8734
8735             E.onAvailable(id, function(){
8736                 var hd = document.getElementsByTagName("head")[0];
8737                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8738                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8739                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8740
8741                 var match;
8742                 while(match = re.exec(html)){
8743                     var attrs = match[1];
8744                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8745                     if(srcMatch && srcMatch[2]){
8746                        var s = document.createElement("script");
8747                        s.src = srcMatch[2];
8748                        var typeMatch = attrs.match(typeRe);
8749                        if(typeMatch && typeMatch[2]){
8750                            s.type = typeMatch[2];
8751                        }
8752                        hd.appendChild(s);
8753                     }else if(match[2] && match[2].length > 0){
8754                         if(window.execScript) {
8755                            window.execScript(match[2]);
8756                         } else {
8757                             /**
8758                              * eval:var:id
8759                              * eval:var:dom
8760                              * eval:var:html
8761                              * 
8762                              */
8763                            window.eval(match[2]);
8764                         }
8765                     }
8766                 }
8767                 var el = document.getElementById(id);
8768                 if(el){el.parentNode.removeChild(el);}
8769                 if(typeof callback == "function"){
8770                     callback();
8771                 }
8772             });
8773             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8774             return this;
8775         },
8776
8777         /**
8778          * Direct access to the UpdateManager update() method (takes the same parameters).
8779          * @param {String/Function} url The url for this request or a function to call to get the url
8780          * @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}
8781          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8782          * @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.
8783          * @return {Roo.Element} this
8784          */
8785         load : function(){
8786             var um = this.getUpdateManager();
8787             um.update.apply(um, arguments);
8788             return this;
8789         },
8790
8791         /**
8792         * Gets this element's UpdateManager
8793         * @return {Roo.UpdateManager} The UpdateManager
8794         */
8795         getUpdateManager : function(){
8796             if(!this.updateManager){
8797                 this.updateManager = new Roo.UpdateManager(this);
8798             }
8799             return this.updateManager;
8800         },
8801
8802         /**
8803          * Disables text selection for this element (normalized across browsers)
8804          * @return {Roo.Element} this
8805          */
8806         unselectable : function(){
8807             this.dom.unselectable = "on";
8808             this.swallowEvent("selectstart", true);
8809             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8810             this.addClass("x-unselectable");
8811             return this;
8812         },
8813
8814         /**
8815         * Calculates the x, y to center this element on the screen
8816         * @return {Array} The x, y values [x, y]
8817         */
8818         getCenterXY : function(){
8819             return this.getAlignToXY(document, 'c-c');
8820         },
8821
8822         /**
8823         * Centers the Element in either the viewport, or another Element.
8824         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8825         */
8826         center : function(centerIn){
8827             this.alignTo(centerIn || document, 'c-c');
8828             return this;
8829         },
8830
8831         /**
8832          * Tests various css rules/browsers to determine if this element uses a border box
8833          * @return {Boolean}
8834          */
8835         isBorderBox : function(){
8836             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8837         },
8838
8839         /**
8840          * Return a box {x, y, width, height} that can be used to set another elements
8841          * size/location to match this element.
8842          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8843          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8844          * @return {Object} box An object in the format {x, y, width, height}
8845          */
8846         getBox : function(contentBox, local){
8847             var xy;
8848             if(!local){
8849                 xy = this.getXY();
8850             }else{
8851                 var left = parseInt(this.getStyle("left"), 10) || 0;
8852                 var top = parseInt(this.getStyle("top"), 10) || 0;
8853                 xy = [left, top];
8854             }
8855             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8856             if(!contentBox){
8857                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8858             }else{
8859                 var l = this.getBorderWidth("l")+this.getPadding("l");
8860                 var r = this.getBorderWidth("r")+this.getPadding("r");
8861                 var t = this.getBorderWidth("t")+this.getPadding("t");
8862                 var b = this.getBorderWidth("b")+this.getPadding("b");
8863                 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)};
8864             }
8865             bx.right = bx.x + bx.width;
8866             bx.bottom = bx.y + bx.height;
8867             return bx;
8868         },
8869
8870         /**
8871          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8872          for more information about the sides.
8873          * @param {String} sides
8874          * @return {Number}
8875          */
8876         getFrameWidth : function(sides, onlyContentBox){
8877             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8878         },
8879
8880         /**
8881          * 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.
8882          * @param {Object} box The box to fill {x, y, width, height}
8883          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8884          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8885          * @return {Roo.Element} this
8886          */
8887         setBox : function(box, adjust, animate){
8888             var w = box.width, h = box.height;
8889             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8890                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8891                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8892             }
8893             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8894             return this;
8895         },
8896
8897         /**
8898          * Forces the browser to repaint this element
8899          * @return {Roo.Element} this
8900          */
8901          repaint : function(){
8902             var dom = this.dom;
8903             this.addClass("x-repaint");
8904             setTimeout(function(){
8905                 Roo.get(dom).removeClass("x-repaint");
8906             }, 1);
8907             return this;
8908         },
8909
8910         /**
8911          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8912          * then it returns the calculated width of the sides (see getPadding)
8913          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8914          * @return {Object/Number}
8915          */
8916         getMargins : function(side){
8917             if(!side){
8918                 return {
8919                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8920                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8921                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8922                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8923                 };
8924             }else{
8925                 return this.addStyles(side, El.margins);
8926              }
8927         },
8928
8929         // private
8930         addStyles : function(sides, styles){
8931             var val = 0, v, w;
8932             for(var i = 0, len = sides.length; i < len; i++){
8933                 v = this.getStyle(styles[sides.charAt(i)]);
8934                 if(v){
8935                      w = parseInt(v, 10);
8936                      if(w){ val += w; }
8937                 }
8938             }
8939             return val;
8940         },
8941
8942         /**
8943          * Creates a proxy element of this element
8944          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8945          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8946          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8947          * @return {Roo.Element} The new proxy element
8948          */
8949         createProxy : function(config, renderTo, matchBox){
8950             if(renderTo){
8951                 renderTo = Roo.getDom(renderTo);
8952             }else{
8953                 renderTo = document.body;
8954             }
8955             config = typeof config == "object" ?
8956                 config : {tag : "div", cls: config};
8957             var proxy = Roo.DomHelper.append(renderTo, config, true);
8958             if(matchBox){
8959                proxy.setBox(this.getBox());
8960             }
8961             return proxy;
8962         },
8963
8964         /**
8965          * Puts a mask over this element to disable user interaction. Requires core.css.
8966          * This method can only be applied to elements which accept child nodes.
8967          * @param {String} msg (optional) A message to display in the mask
8968          * @param {String} msgCls (optional) A css class to apply to the msg element
8969          * @return {Element} The mask  element
8970          */
8971         mask : function(msg, msgCls)
8972         {
8973             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8974                 this.setStyle("position", "relative");
8975             }
8976             if(!this._mask){
8977                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8978             }
8979             this.addClass("x-masked");
8980             this._mask.setDisplayed(true);
8981             
8982             // we wander
8983             var z = 0;
8984             var dom = this.dom
8985             while (dom && dom.style) {
8986                 if (!isNaN(parseInt(dom.style.zIndex))) {
8987                     z = Math.max(z, parseInt(dom.style.zIndex));
8988                 }
8989                 dom = dom.parentNode;
8990             }
8991             // if we are masking the body - then it hides everything..
8992             if (this.dom == document.body) {
8993                 z = 1000000;
8994                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8995                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8996             }
8997            
8998             if(typeof msg == 'string'){
8999                 if(!this._maskMsg){
9000                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9001                 }
9002                 var mm = this._maskMsg;
9003                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9004                 mm.dom.firstChild.innerHTML = msg;
9005                 mm.setDisplayed(true);
9006                 mm.center(this);
9007                 mm.setStyle('z-index', z + 102);
9008             }
9009             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9010                 this._mask.setHeight(this.getHeight());
9011             }
9012             this._mask.setStyle('z-index', z + 100);
9013             
9014             return this._mask;
9015         },
9016
9017         /**
9018          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9019          * it is cached for reuse.
9020          */
9021         unmask : function(removeEl){
9022             if(this._mask){
9023                 if(removeEl === true){
9024                     this._mask.remove();
9025                     delete this._mask;
9026                     if(this._maskMsg){
9027                         this._maskMsg.remove();
9028                         delete this._maskMsg;
9029                     }
9030                 }else{
9031                     this._mask.setDisplayed(false);
9032                     if(this._maskMsg){
9033                         this._maskMsg.setDisplayed(false);
9034                     }
9035                 }
9036             }
9037             this.removeClass("x-masked");
9038         },
9039
9040         /**
9041          * Returns true if this element is masked
9042          * @return {Boolean}
9043          */
9044         isMasked : function(){
9045             return this._mask && this._mask.isVisible();
9046         },
9047
9048         /**
9049          * Creates an iframe shim for this element to keep selects and other windowed objects from
9050          * showing through.
9051          * @return {Roo.Element} The new shim element
9052          */
9053         createShim : function(){
9054             var el = document.createElement('iframe');
9055             el.frameBorder = 'no';
9056             el.className = 'roo-shim';
9057             if(Roo.isIE && Roo.isSecure){
9058                 el.src = Roo.SSL_SECURE_URL;
9059             }
9060             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9061             shim.autoBoxAdjust = false;
9062             return shim;
9063         },
9064
9065         /**
9066          * Removes this element from the DOM and deletes it from the cache
9067          */
9068         remove : function(){
9069             if(this.dom.parentNode){
9070                 this.dom.parentNode.removeChild(this.dom);
9071             }
9072             delete El.cache[this.dom.id];
9073         },
9074
9075         /**
9076          * Sets up event handlers to add and remove a css class when the mouse is over this element
9077          * @param {String} className
9078          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9079          * mouseout events for children elements
9080          * @return {Roo.Element} this
9081          */
9082         addClassOnOver : function(className, preventFlicker){
9083             this.on("mouseover", function(){
9084                 Roo.fly(this, '_internal').addClass(className);
9085             }, this.dom);
9086             var removeFn = function(e){
9087                 if(preventFlicker !== true || !e.within(this, true)){
9088                     Roo.fly(this, '_internal').removeClass(className);
9089                 }
9090             };
9091             this.on("mouseout", removeFn, this.dom);
9092             return this;
9093         },
9094
9095         /**
9096          * Sets up event handlers to add and remove a css class when this element has the focus
9097          * @param {String} className
9098          * @return {Roo.Element} this
9099          */
9100         addClassOnFocus : function(className){
9101             this.on("focus", function(){
9102                 Roo.fly(this, '_internal').addClass(className);
9103             }, this.dom);
9104             this.on("blur", function(){
9105                 Roo.fly(this, '_internal').removeClass(className);
9106             }, this.dom);
9107             return this;
9108         },
9109         /**
9110          * 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)
9111          * @param {String} className
9112          * @return {Roo.Element} this
9113          */
9114         addClassOnClick : function(className){
9115             var dom = this.dom;
9116             this.on("mousedown", function(){
9117                 Roo.fly(dom, '_internal').addClass(className);
9118                 var d = Roo.get(document);
9119                 var fn = function(){
9120                     Roo.fly(dom, '_internal').removeClass(className);
9121                     d.removeListener("mouseup", fn);
9122                 };
9123                 d.on("mouseup", fn);
9124             });
9125             return this;
9126         },
9127
9128         /**
9129          * Stops the specified event from bubbling and optionally prevents the default action
9130          * @param {String} eventName
9131          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9132          * @return {Roo.Element} this
9133          */
9134         swallowEvent : function(eventName, preventDefault){
9135             var fn = function(e){
9136                 e.stopPropagation();
9137                 if(preventDefault){
9138                     e.preventDefault();
9139                 }
9140             };
9141             if(eventName instanceof Array){
9142                 for(var i = 0, len = eventName.length; i < len; i++){
9143                      this.on(eventName[i], fn);
9144                 }
9145                 return this;
9146             }
9147             this.on(eventName, fn);
9148             return this;
9149         },
9150
9151         /**
9152          * @private
9153          */
9154       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9155
9156         /**
9157          * Sizes this element to its parent element's dimensions performing
9158          * neccessary box adjustments.
9159          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9160          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9161          * @return {Roo.Element} this
9162          */
9163         fitToParent : function(monitorResize, targetParent) {
9164           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9165           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9166           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9167             return;
9168           }
9169           var p = Roo.get(targetParent || this.dom.parentNode);
9170           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9171           if (monitorResize === true) {
9172             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9173             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9174           }
9175           return this;
9176         },
9177
9178         /**
9179          * Gets the next sibling, skipping text nodes
9180          * @return {HTMLElement} The next sibling or null
9181          */
9182         getNextSibling : function(){
9183             var n = this.dom.nextSibling;
9184             while(n && n.nodeType != 1){
9185                 n = n.nextSibling;
9186             }
9187             return n;
9188         },
9189
9190         /**
9191          * Gets the previous sibling, skipping text nodes
9192          * @return {HTMLElement} The previous sibling or null
9193          */
9194         getPrevSibling : function(){
9195             var n = this.dom.previousSibling;
9196             while(n && n.nodeType != 1){
9197                 n = n.previousSibling;
9198             }
9199             return n;
9200         },
9201
9202
9203         /**
9204          * Appends the passed element(s) to this element
9205          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9206          * @return {Roo.Element} this
9207          */
9208         appendChild: function(el){
9209             el = Roo.get(el);
9210             el.appendTo(this);
9211             return this;
9212         },
9213
9214         /**
9215          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9216          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9217          * automatically generated with the specified attributes.
9218          * @param {HTMLElement} insertBefore (optional) a child element of this element
9219          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9220          * @return {Roo.Element} The new child element
9221          */
9222         createChild: function(config, insertBefore, returnDom){
9223             config = config || {tag:'div'};
9224             if(insertBefore){
9225                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9226             }
9227             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9228         },
9229
9230         /**
9231          * Appends this element to the passed element
9232          * @param {String/HTMLElement/Element} el The new parent element
9233          * @return {Roo.Element} this
9234          */
9235         appendTo: function(el){
9236             el = Roo.getDom(el);
9237             el.appendChild(this.dom);
9238             return this;
9239         },
9240
9241         /**
9242          * Inserts this element before the passed element in the DOM
9243          * @param {String/HTMLElement/Element} el The element to insert before
9244          * @return {Roo.Element} this
9245          */
9246         insertBefore: function(el){
9247             el = Roo.getDom(el);
9248             el.parentNode.insertBefore(this.dom, el);
9249             return this;
9250         },
9251
9252         /**
9253          * Inserts this element after the passed element in the DOM
9254          * @param {String/HTMLElement/Element} el The element to insert after
9255          * @return {Roo.Element} this
9256          */
9257         insertAfter: function(el){
9258             el = Roo.getDom(el);
9259             el.parentNode.insertBefore(this.dom, el.nextSibling);
9260             return this;
9261         },
9262
9263         /**
9264          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9265          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9266          * @return {Roo.Element} The new child
9267          */
9268         insertFirst: function(el, returnDom){
9269             el = el || {};
9270             if(typeof el == 'object' && !el.nodeType){ // dh config
9271                 return this.createChild(el, this.dom.firstChild, returnDom);
9272             }else{
9273                 el = Roo.getDom(el);
9274                 this.dom.insertBefore(el, this.dom.firstChild);
9275                 return !returnDom ? Roo.get(el) : el;
9276             }
9277         },
9278
9279         /**
9280          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9281          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9282          * @param {String} where (optional) 'before' or 'after' defaults to before
9283          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9284          * @return {Roo.Element} the inserted Element
9285          */
9286         insertSibling: function(el, where, returnDom){
9287             where = where ? where.toLowerCase() : 'before';
9288             el = el || {};
9289             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9290
9291             if(typeof el == 'object' && !el.nodeType){ // dh config
9292                 if(where == 'after' && !this.dom.nextSibling){
9293                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9294                 }else{
9295                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9296                 }
9297
9298             }else{
9299                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9300                             where == 'before' ? this.dom : this.dom.nextSibling);
9301                 if(!returnDom){
9302                     rt = Roo.get(rt);
9303                 }
9304             }
9305             return rt;
9306         },
9307
9308         /**
9309          * Creates and wraps this element with another element
9310          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9311          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9312          * @return {HTMLElement/Element} The newly created wrapper element
9313          */
9314         wrap: function(config, returnDom){
9315             if(!config){
9316                 config = {tag: "div"};
9317             }
9318             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9319             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9320             return newEl;
9321         },
9322
9323         /**
9324          * Replaces the passed element with this element
9325          * @param {String/HTMLElement/Element} el The element to replace
9326          * @return {Roo.Element} this
9327          */
9328         replace: function(el){
9329             el = Roo.get(el);
9330             this.insertBefore(el);
9331             el.remove();
9332             return this;
9333         },
9334
9335         /**
9336          * Inserts an html fragment into this element
9337          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9338          * @param {String} html The HTML fragment
9339          * @param {Boolean} returnEl True to return an Roo.Element
9340          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9341          */
9342         insertHtml : function(where, html, returnEl){
9343             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9344             return returnEl ? Roo.get(el) : el;
9345         },
9346
9347         /**
9348          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9349          * @param {Object} o The object with the attributes
9350          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9351          * @return {Roo.Element} this
9352          */
9353         set : function(o, useSet){
9354             var el = this.dom;
9355             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9356             for(var attr in o){
9357                 if(attr == "style" || typeof o[attr] == "function") continue;
9358                 if(attr=="cls"){
9359                     el.className = o["cls"];
9360                 }else{
9361                     if(useSet) el.setAttribute(attr, o[attr]);
9362                     else el[attr] = o[attr];
9363                 }
9364             }
9365             if(o.style){
9366                 Roo.DomHelper.applyStyles(el, o.style);
9367             }
9368             return this;
9369         },
9370
9371         /**
9372          * Convenience method for constructing a KeyMap
9373          * @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:
9374          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9375          * @param {Function} fn The function to call
9376          * @param {Object} scope (optional) The scope of the function
9377          * @return {Roo.KeyMap} The KeyMap created
9378          */
9379         addKeyListener : function(key, fn, scope){
9380             var config;
9381             if(typeof key != "object" || key instanceof Array){
9382                 config = {
9383                     key: key,
9384                     fn: fn,
9385                     scope: scope
9386                 };
9387             }else{
9388                 config = {
9389                     key : key.key,
9390                     shift : key.shift,
9391                     ctrl : key.ctrl,
9392                     alt : key.alt,
9393                     fn: fn,
9394                     scope: scope
9395                 };
9396             }
9397             return new Roo.KeyMap(this, config);
9398         },
9399
9400         /**
9401          * Creates a KeyMap for this element
9402          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9403          * @return {Roo.KeyMap} The KeyMap created
9404          */
9405         addKeyMap : function(config){
9406             return new Roo.KeyMap(this, config);
9407         },
9408
9409         /**
9410          * Returns true if this element is scrollable.
9411          * @return {Boolean}
9412          */
9413          isScrollable : function(){
9414             var dom = this.dom;
9415             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9416         },
9417
9418         /**
9419          * 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().
9420          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9421          * @param {Number} value The new scroll value
9422          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9423          * @return {Element} this
9424          */
9425
9426         scrollTo : function(side, value, animate){
9427             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9428             if(!animate || !A){
9429                 this.dom[prop] = value;
9430             }else{
9431                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9432                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9433             }
9434             return this;
9435         },
9436
9437         /**
9438          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9439          * within this element's scrollable range.
9440          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9441          * @param {Number} distance How far to scroll the element in pixels
9442          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9443          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9444          * was scrolled as far as it could go.
9445          */
9446          scroll : function(direction, distance, animate){
9447              if(!this.isScrollable()){
9448                  return;
9449              }
9450              var el = this.dom;
9451              var l = el.scrollLeft, t = el.scrollTop;
9452              var w = el.scrollWidth, h = el.scrollHeight;
9453              var cw = el.clientWidth, ch = el.clientHeight;
9454              direction = direction.toLowerCase();
9455              var scrolled = false;
9456              var a = this.preanim(arguments, 2);
9457              switch(direction){
9458                  case "l":
9459                  case "left":
9460                      if(w - l > cw){
9461                          var v = Math.min(l + distance, w-cw);
9462                          this.scrollTo("left", v, a);
9463                          scrolled = true;
9464                      }
9465                      break;
9466                 case "r":
9467                 case "right":
9468                      if(l > 0){
9469                          var v = Math.max(l - distance, 0);
9470                          this.scrollTo("left", v, a);
9471                          scrolled = true;
9472                      }
9473                      break;
9474                 case "t":
9475                 case "top":
9476                 case "up":
9477                      if(t > 0){
9478                          var v = Math.max(t - distance, 0);
9479                          this.scrollTo("top", v, a);
9480                          scrolled = true;
9481                      }
9482                      break;
9483                 case "b":
9484                 case "bottom":
9485                 case "down":
9486                      if(h - t > ch){
9487                          var v = Math.min(t + distance, h-ch);
9488                          this.scrollTo("top", v, a);
9489                          scrolled = true;
9490                      }
9491                      break;
9492              }
9493              return scrolled;
9494         },
9495
9496         /**
9497          * Translates the passed page coordinates into left/top css values for this element
9498          * @param {Number/Array} x The page x or an array containing [x, y]
9499          * @param {Number} y The page y
9500          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9501          */
9502         translatePoints : function(x, y){
9503             if(typeof x == 'object' || x instanceof Array){
9504                 y = x[1]; x = x[0];
9505             }
9506             var p = this.getStyle('position');
9507             var o = this.getXY();
9508
9509             var l = parseInt(this.getStyle('left'), 10);
9510             var t = parseInt(this.getStyle('top'), 10);
9511
9512             if(isNaN(l)){
9513                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9514             }
9515             if(isNaN(t)){
9516                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9517             }
9518
9519             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9520         },
9521
9522         /**
9523          * Returns the current scroll position of the element.
9524          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9525          */
9526         getScroll : function(){
9527             var d = this.dom, doc = document;
9528             if(d == doc || d == doc.body){
9529                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9530                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9531                 return {left: l, top: t};
9532             }else{
9533                 return {left: d.scrollLeft, top: d.scrollTop};
9534             }
9535         },
9536
9537         /**
9538          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9539          * are convert to standard 6 digit hex color.
9540          * @param {String} attr The css attribute
9541          * @param {String} defaultValue The default value to use when a valid color isn't found
9542          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9543          * YUI color anims.
9544          */
9545         getColor : function(attr, defaultValue, prefix){
9546             var v = this.getStyle(attr);
9547             if(!v || v == "transparent" || v == "inherit") {
9548                 return defaultValue;
9549             }
9550             var color = typeof prefix == "undefined" ? "#" : prefix;
9551             if(v.substr(0, 4) == "rgb("){
9552                 var rvs = v.slice(4, v.length -1).split(",");
9553                 for(var i = 0; i < 3; i++){
9554                     var h = parseInt(rvs[i]).toString(16);
9555                     if(h < 16){
9556                         h = "0" + h;
9557                     }
9558                     color += h;
9559                 }
9560             } else {
9561                 if(v.substr(0, 1) == "#"){
9562                     if(v.length == 4) {
9563                         for(var i = 1; i < 4; i++){
9564                             var c = v.charAt(i);
9565                             color +=  c + c;
9566                         }
9567                     }else if(v.length == 7){
9568                         color += v.substr(1);
9569                     }
9570                 }
9571             }
9572             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9573         },
9574
9575         /**
9576          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9577          * gradient background, rounded corners and a 4-way shadow.
9578          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9579          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9580          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9581          * @return {Roo.Element} this
9582          */
9583         boxWrap : function(cls){
9584             cls = cls || 'x-box';
9585             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9586             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9587             return el;
9588         },
9589
9590         /**
9591          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9592          * @param {String} namespace The namespace in which to look for the attribute
9593          * @param {String} name The attribute name
9594          * @return {String} The attribute value
9595          */
9596         getAttributeNS : Roo.isIE ? function(ns, name){
9597             var d = this.dom;
9598             var type = typeof d[ns+":"+name];
9599             if(type != 'undefined' && type != 'unknown'){
9600                 return d[ns+":"+name];
9601             }
9602             return d[name];
9603         } : function(ns, name){
9604             var d = this.dom;
9605             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9606         },
9607         
9608         
9609         /**
9610          * Sets or Returns the value the dom attribute value
9611          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9612          * @param {String} value (optional) The value to set the attribute to
9613          * @return {String} The attribute value
9614          */
9615         attr : function(name){
9616             if (arguments.length > 1) {
9617                 this.dom.setAttribute(name, arguments[1]);
9618                 return arguments[1];
9619             }
9620             if (typeof(name) == 'object') {
9621                 for(var i in name) {
9622                     this.attr(i, name[i]);
9623                 }
9624                 return name;
9625             }
9626             
9627             
9628             if (!this.dom.hasAttribute(name)) {
9629                 return undefined;
9630             }
9631             return this.dom.getAttribute(name);
9632         }
9633         
9634         
9635         
9636     };
9637
9638     var ep = El.prototype;
9639
9640     /**
9641      * Appends an event handler (Shorthand for addListener)
9642      * @param {String}   eventName     The type of event to append
9643      * @param {Function} fn        The method the event invokes
9644      * @param {Object} scope       (optional) The scope (this object) of the fn
9645      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9646      * @method
9647      */
9648     ep.on = ep.addListener;
9649         // backwards compat
9650     ep.mon = ep.addListener;
9651
9652     /**
9653      * Removes an event handler from this element (shorthand for removeListener)
9654      * @param {String} eventName the type of event to remove
9655      * @param {Function} fn the method the event invokes
9656      * @return {Roo.Element} this
9657      * @method
9658      */
9659     ep.un = ep.removeListener;
9660
9661     /**
9662      * true to automatically adjust width and height settings for box-model issues (default to true)
9663      */
9664     ep.autoBoxAdjust = true;
9665
9666     // private
9667     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9668
9669     // private
9670     El.addUnits = function(v, defaultUnit){
9671         if(v === "" || v == "auto"){
9672             return v;
9673         }
9674         if(v === undefined){
9675             return '';
9676         }
9677         if(typeof v == "number" || !El.unitPattern.test(v)){
9678             return v + (defaultUnit || 'px');
9679         }
9680         return v;
9681     };
9682
9683     // special markup used throughout Roo when box wrapping elements
9684     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>';
9685     /**
9686      * Visibility mode constant - Use visibility to hide element
9687      * @static
9688      * @type Number
9689      */
9690     El.VISIBILITY = 1;
9691     /**
9692      * Visibility mode constant - Use display to hide element
9693      * @static
9694      * @type Number
9695      */
9696     El.DISPLAY = 2;
9697
9698     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9699     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9700     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9701
9702
9703
9704     /**
9705      * @private
9706      */
9707     El.cache = {};
9708
9709     var docEl;
9710
9711     /**
9712      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9713      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9714      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9715      * @return {Element} The Element object
9716      * @static
9717      */
9718     El.get = function(el){
9719         var ex, elm, id;
9720         if(!el){ return null; }
9721         if(typeof el == "string"){ // element id
9722             if(!(elm = document.getElementById(el))){
9723                 return null;
9724             }
9725             if(ex = El.cache[el]){
9726                 ex.dom = elm;
9727             }else{
9728                 ex = El.cache[el] = new El(elm);
9729             }
9730             return ex;
9731         }else if(el.tagName){ // dom element
9732             if(!(id = el.id)){
9733                 id = Roo.id(el);
9734             }
9735             if(ex = El.cache[id]){
9736                 ex.dom = el;
9737             }else{
9738                 ex = El.cache[id] = new El(el);
9739             }
9740             return ex;
9741         }else if(el instanceof El){
9742             if(el != docEl){
9743                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9744                                                               // catch case where it hasn't been appended
9745                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9746             }
9747             return el;
9748         }else if(el.isComposite){
9749             return el;
9750         }else if(el instanceof Array){
9751             return El.select(el);
9752         }else if(el == document){
9753             // create a bogus element object representing the document object
9754             if(!docEl){
9755                 var f = function(){};
9756                 f.prototype = El.prototype;
9757                 docEl = new f();
9758                 docEl.dom = document;
9759             }
9760             return docEl;
9761         }
9762         return null;
9763     };
9764
9765     // private
9766     El.uncache = function(el){
9767         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9768             if(a[i]){
9769                 delete El.cache[a[i].id || a[i]];
9770             }
9771         }
9772     };
9773
9774     // private
9775     // Garbage collection - uncache elements/purge listeners on orphaned elements
9776     // so we don't hold a reference and cause the browser to retain them
9777     El.garbageCollect = function(){
9778         if(!Roo.enableGarbageCollector){
9779             clearInterval(El.collectorThread);
9780             return;
9781         }
9782         for(var eid in El.cache){
9783             var el = El.cache[eid], d = el.dom;
9784             // -------------------------------------------------------
9785             // Determining what is garbage:
9786             // -------------------------------------------------------
9787             // !d
9788             // dom node is null, definitely garbage
9789             // -------------------------------------------------------
9790             // !d.parentNode
9791             // no parentNode == direct orphan, definitely garbage
9792             // -------------------------------------------------------
9793             // !d.offsetParent && !document.getElementById(eid)
9794             // display none elements have no offsetParent so we will
9795             // also try to look it up by it's id. However, check
9796             // offsetParent first so we don't do unneeded lookups.
9797             // This enables collection of elements that are not orphans
9798             // directly, but somewhere up the line they have an orphan
9799             // parent.
9800             // -------------------------------------------------------
9801             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9802                 delete El.cache[eid];
9803                 if(d && Roo.enableListenerCollection){
9804                     E.purgeElement(d);
9805                 }
9806             }
9807         }
9808     }
9809     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9810
9811
9812     // dom is optional
9813     El.Flyweight = function(dom){
9814         this.dom = dom;
9815     };
9816     El.Flyweight.prototype = El.prototype;
9817
9818     El._flyweights = {};
9819     /**
9820      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9821      * the dom node can be overwritten by other code.
9822      * @param {String/HTMLElement} el The dom node or id
9823      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9824      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9825      * @static
9826      * @return {Element} The shared Element object
9827      */
9828     El.fly = function(el, named){
9829         named = named || '_global';
9830         el = Roo.getDom(el);
9831         if(!el){
9832             return null;
9833         }
9834         if(!El._flyweights[named]){
9835             El._flyweights[named] = new El.Flyweight();
9836         }
9837         El._flyweights[named].dom = el;
9838         return El._flyweights[named];
9839     };
9840
9841     /**
9842      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9843      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9844      * Shorthand of {@link Roo.Element#get}
9845      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9846      * @return {Element} The Element object
9847      * @member Roo
9848      * @method get
9849      */
9850     Roo.get = El.get;
9851     /**
9852      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9853      * the dom node can be overwritten by other code.
9854      * Shorthand of {@link Roo.Element#fly}
9855      * @param {String/HTMLElement} el The dom node or id
9856      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9857      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9858      * @static
9859      * @return {Element} The shared Element object
9860      * @member Roo
9861      * @method fly
9862      */
9863     Roo.fly = El.fly;
9864
9865     // speedy lookup for elements never to box adjust
9866     var noBoxAdjust = Roo.isStrict ? {
9867         select:1
9868     } : {
9869         input:1, select:1, textarea:1
9870     };
9871     if(Roo.isIE || Roo.isGecko){
9872         noBoxAdjust['button'] = 1;
9873     }
9874
9875
9876     Roo.EventManager.on(window, 'unload', function(){
9877         delete El.cache;
9878         delete El._flyweights;
9879     });
9880 })();
9881
9882
9883
9884
9885 if(Roo.DomQuery){
9886     Roo.Element.selectorFunction = Roo.DomQuery.select;
9887 }
9888
9889 Roo.Element.select = function(selector, unique, root){
9890     var els;
9891     if(typeof selector == "string"){
9892         els = Roo.Element.selectorFunction(selector, root);
9893     }else if(selector.length !== undefined){
9894         els = selector;
9895     }else{
9896         throw "Invalid selector";
9897     }
9898     if(unique === true){
9899         return new Roo.CompositeElement(els);
9900     }else{
9901         return new Roo.CompositeElementLite(els);
9902     }
9903 };
9904 /**
9905  * Selects elements based on the passed CSS selector to enable working on them as 1.
9906  * @param {String/Array} selector The CSS selector or an array of elements
9907  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9908  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9909  * @return {CompositeElementLite/CompositeElement}
9910  * @member Roo
9911  * @method select
9912  */
9913 Roo.select = Roo.Element.select;
9914
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928 /*
9929  * Based on:
9930  * Ext JS Library 1.1.1
9931  * Copyright(c) 2006-2007, Ext JS, LLC.
9932  *
9933  * Originally Released Under LGPL - original licence link has changed is not relivant.
9934  *
9935  * Fork - LGPL
9936  * <script type="text/javascript">
9937  */
9938
9939
9940
9941 //Notifies Element that fx methods are available
9942 Roo.enableFx = true;
9943
9944 /**
9945  * @class Roo.Fx
9946  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9947  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9948  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9949  * Element effects to work.</p><br/>
9950  *
9951  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9952  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9953  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9954  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9955  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9956  * expected results and should be done with care.</p><br/>
9957  *
9958  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9959  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9960 <pre>
9961 Value  Description
9962 -----  -----------------------------
9963 tl     The top left corner
9964 t      The center of the top edge
9965 tr     The top right corner
9966 l      The center of the left edge
9967 r      The center of the right edge
9968 bl     The bottom left corner
9969 b      The center of the bottom edge
9970 br     The bottom right corner
9971 </pre>
9972  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9973  * below are common options that can be passed to any Fx method.</b>
9974  * @cfg {Function} callback A function called when the effect is finished
9975  * @cfg {Object} scope The scope of the effect function
9976  * @cfg {String} easing A valid Easing value for the effect
9977  * @cfg {String} afterCls A css class to apply after the effect
9978  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9979  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9980  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9981  * effects that end with the element being visually hidden, ignored otherwise)
9982  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9983  * a function which returns such a specification that will be applied to the Element after the effect finishes
9984  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9985  * @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
9986  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9987  */
9988 Roo.Fx = {
9989         /**
9990          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9991          * origin for the slide effect.  This function automatically handles wrapping the element with
9992          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9993          * Usage:
9994          *<pre><code>
9995 // default: slide the element in from the top
9996 el.slideIn();
9997
9998 // custom: slide the element in from the right with a 2-second duration
9999 el.slideIn('r', { duration: 2 });
10000
10001 // common config options shown with default values
10002 el.slideIn('t', {
10003     easing: 'easeOut',
10004     duration: .5
10005 });
10006 </code></pre>
10007          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10008          * @param {Object} options (optional) Object literal with any of the Fx config options
10009          * @return {Roo.Element} The Element
10010          */
10011     slideIn : function(anchor, o){
10012         var el = this.getFxEl();
10013         o = o || {};
10014
10015         el.queueFx(o, function(){
10016
10017             anchor = anchor || "t";
10018
10019             // fix display to visibility
10020             this.fixDisplay();
10021
10022             // restore values after effect
10023             var r = this.getFxRestore();
10024             var b = this.getBox();
10025             // fixed size for slide
10026             this.setSize(b);
10027
10028             // wrap if needed
10029             var wrap = this.fxWrap(r.pos, o, "hidden");
10030
10031             var st = this.dom.style;
10032             st.visibility = "visible";
10033             st.position = "absolute";
10034
10035             // clear out temp styles after slide and unwrap
10036             var after = function(){
10037                 el.fxUnwrap(wrap, r.pos, o);
10038                 st.width = r.width;
10039                 st.height = r.height;
10040                 el.afterFx(o);
10041             };
10042             // time to calc the positions
10043             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10044
10045             switch(anchor.toLowerCase()){
10046                 case "t":
10047                     wrap.setSize(b.width, 0);
10048                     st.left = st.bottom = "0";
10049                     a = {height: bh};
10050                 break;
10051                 case "l":
10052                     wrap.setSize(0, b.height);
10053                     st.right = st.top = "0";
10054                     a = {width: bw};
10055                 break;
10056                 case "r":
10057                     wrap.setSize(0, b.height);
10058                     wrap.setX(b.right);
10059                     st.left = st.top = "0";
10060                     a = {width: bw, points: pt};
10061                 break;
10062                 case "b":
10063                     wrap.setSize(b.width, 0);
10064                     wrap.setY(b.bottom);
10065                     st.left = st.top = "0";
10066                     a = {height: bh, points: pt};
10067                 break;
10068                 case "tl":
10069                     wrap.setSize(0, 0);
10070                     st.right = st.bottom = "0";
10071                     a = {width: bw, height: bh};
10072                 break;
10073                 case "bl":
10074                     wrap.setSize(0, 0);
10075                     wrap.setY(b.y+b.height);
10076                     st.right = st.top = "0";
10077                     a = {width: bw, height: bh, points: pt};
10078                 break;
10079                 case "br":
10080                     wrap.setSize(0, 0);
10081                     wrap.setXY([b.right, b.bottom]);
10082                     st.left = st.top = "0";
10083                     a = {width: bw, height: bh, points: pt};
10084                 break;
10085                 case "tr":
10086                     wrap.setSize(0, 0);
10087                     wrap.setX(b.x+b.width);
10088                     st.left = st.bottom = "0";
10089                     a = {width: bw, height: bh, points: pt};
10090                 break;
10091             }
10092             this.dom.style.visibility = "visible";
10093             wrap.show();
10094
10095             arguments.callee.anim = wrap.fxanim(a,
10096                 o,
10097                 'motion',
10098                 .5,
10099                 'easeOut', after);
10100         });
10101         return this;
10102     },
10103     
10104         /**
10105          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10106          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10107          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10108          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10109          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10110          * Usage:
10111          *<pre><code>
10112 // default: slide the element out to the top
10113 el.slideOut();
10114
10115 // custom: slide the element out to the right with a 2-second duration
10116 el.slideOut('r', { duration: 2 });
10117
10118 // common config options shown with default values
10119 el.slideOut('t', {
10120     easing: 'easeOut',
10121     duration: .5,
10122     remove: false,
10123     useDisplay: false
10124 });
10125 </code></pre>
10126          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10127          * @param {Object} options (optional) Object literal with any of the Fx config options
10128          * @return {Roo.Element} The Element
10129          */
10130     slideOut : function(anchor, o){
10131         var el = this.getFxEl();
10132         o = o || {};
10133
10134         el.queueFx(o, function(){
10135
10136             anchor = anchor || "t";
10137
10138             // restore values after effect
10139             var r = this.getFxRestore();
10140             
10141             var b = this.getBox();
10142             // fixed size for slide
10143             this.setSize(b);
10144
10145             // wrap if needed
10146             var wrap = this.fxWrap(r.pos, o, "visible");
10147
10148             var st = this.dom.style;
10149             st.visibility = "visible";
10150             st.position = "absolute";
10151
10152             wrap.setSize(b);
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.fxUnwrap(wrap, r.pos, o);
10162
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             var a, zero = {to: 0};
10170             switch(anchor.toLowerCase()){
10171                 case "t":
10172                     st.left = st.bottom = "0";
10173                     a = {height: zero};
10174                 break;
10175                 case "l":
10176                     st.right = st.top = "0";
10177                     a = {width: zero};
10178                 break;
10179                 case "r":
10180                     st.left = st.top = "0";
10181                     a = {width: zero, points: {to:[b.right, b.y]}};
10182                 break;
10183                 case "b":
10184                     st.left = st.top = "0";
10185                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10186                 break;
10187                 case "tl":
10188                     st.right = st.bottom = "0";
10189                     a = {width: zero, height: zero};
10190                 break;
10191                 case "bl":
10192                     st.right = st.top = "0";
10193                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10194                 break;
10195                 case "br":
10196                     st.left = st.top = "0";
10197                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10198                 break;
10199                 case "tr":
10200                     st.left = st.bottom = "0";
10201                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10202                 break;
10203             }
10204
10205             arguments.callee.anim = wrap.fxanim(a,
10206                 o,
10207                 'motion',
10208                 .5,
10209                 "easeOut", after);
10210         });
10211         return this;
10212     },
10213
10214         /**
10215          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10216          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10217          * The element must be removed from the DOM using the 'remove' config option if desired.
10218          * Usage:
10219          *<pre><code>
10220 // default
10221 el.puff();
10222
10223 // common config options shown with default values
10224 el.puff({
10225     easing: 'easeOut',
10226     duration: .5,
10227     remove: false,
10228     useDisplay: false
10229 });
10230 </code></pre>
10231          * @param {Object} options (optional) Object literal with any of the Fx config options
10232          * @return {Roo.Element} The Element
10233          */
10234     puff : function(o){
10235         var el = this.getFxEl();
10236         o = o || {};
10237
10238         el.queueFx(o, function(){
10239             this.clearOpacity();
10240             this.show();
10241
10242             // restore values after effect
10243             var r = this.getFxRestore();
10244             var st = this.dom.style;
10245
10246             var after = function(){
10247                 if(o.useDisplay){
10248                     el.setDisplayed(false);
10249                 }else{
10250                     el.hide();
10251                 }
10252
10253                 el.clearOpacity();
10254
10255                 el.setPositioning(r.pos);
10256                 st.width = r.width;
10257                 st.height = r.height;
10258                 st.fontSize = '';
10259                 el.afterFx(o);
10260             };
10261
10262             var width = this.getWidth();
10263             var height = this.getHeight();
10264
10265             arguments.callee.anim = this.fxanim({
10266                     width : {to: this.adjustWidth(width * 2)},
10267                     height : {to: this.adjustHeight(height * 2)},
10268                     points : {by: [-(width * .5), -(height * .5)]},
10269                     opacity : {to: 0},
10270                     fontSize: {to:200, unit: "%"}
10271                 },
10272                 o,
10273                 'motion',
10274                 .5,
10275                 "easeOut", after);
10276         });
10277         return this;
10278     },
10279
10280         /**
10281          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10282          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10283          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10284          * Usage:
10285          *<pre><code>
10286 // default
10287 el.switchOff();
10288
10289 // all config options shown with default values
10290 el.switchOff({
10291     easing: 'easeIn',
10292     duration: .3,
10293     remove: false,
10294     useDisplay: false
10295 });
10296 </code></pre>
10297          * @param {Object} options (optional) Object literal with any of the Fx config options
10298          * @return {Roo.Element} The Element
10299          */
10300     switchOff : function(o){
10301         var el = this.getFxEl();
10302         o = o || {};
10303
10304         el.queueFx(o, function(){
10305             this.clearOpacity();
10306             this.clip();
10307
10308             // restore values after effect
10309             var r = this.getFxRestore();
10310             var st = this.dom.style;
10311
10312             var after = function(){
10313                 if(o.useDisplay){
10314                     el.setDisplayed(false);
10315                 }else{
10316                     el.hide();
10317                 }
10318
10319                 el.clearOpacity();
10320                 el.setPositioning(r.pos);
10321                 st.width = r.width;
10322                 st.height = r.height;
10323
10324                 el.afterFx(o);
10325             };
10326
10327             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10328                 this.clearOpacity();
10329                 (function(){
10330                     this.fxanim({
10331                         height:{to:1},
10332                         points:{by:[0, this.getHeight() * .5]}
10333                     }, o, 'motion', 0.3, 'easeIn', after);
10334                 }).defer(100, this);
10335             });
10336         });
10337         return this;
10338     },
10339
10340     /**
10341      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10342      * changed using the "attr" config option) and then fading back to the original color. If no original
10343      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10344      * Usage:
10345 <pre><code>
10346 // default: highlight background to yellow
10347 el.highlight();
10348
10349 // custom: highlight foreground text to blue for 2 seconds
10350 el.highlight("0000ff", { attr: 'color', duration: 2 });
10351
10352 // common config options shown with default values
10353 el.highlight("ffff9c", {
10354     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10355     endColor: (current color) or "ffffff",
10356     easing: 'easeIn',
10357     duration: 1
10358 });
10359 </code></pre>
10360      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10361      * @param {Object} options (optional) Object literal with any of the Fx config options
10362      * @return {Roo.Element} The Element
10363      */ 
10364     highlight : function(color, o){
10365         var el = this.getFxEl();
10366         o = o || {};
10367
10368         el.queueFx(o, function(){
10369             color = color || "ffff9c";
10370             attr = o.attr || "backgroundColor";
10371
10372             this.clearOpacity();
10373             this.show();
10374
10375             var origColor = this.getColor(attr);
10376             var restoreColor = this.dom.style[attr];
10377             endColor = (o.endColor || origColor) || "ffffff";
10378
10379             var after = function(){
10380                 el.dom.style[attr] = restoreColor;
10381                 el.afterFx(o);
10382             };
10383
10384             var a = {};
10385             a[attr] = {from: color, to: endColor};
10386             arguments.callee.anim = this.fxanim(a,
10387                 o,
10388                 'color',
10389                 1,
10390                 'easeIn', after);
10391         });
10392         return this;
10393     },
10394
10395    /**
10396     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10397     * Usage:
10398 <pre><code>
10399 // default: a single light blue ripple
10400 el.frame();
10401
10402 // custom: 3 red ripples lasting 3 seconds total
10403 el.frame("ff0000", 3, { duration: 3 });
10404
10405 // common config options shown with default values
10406 el.frame("C3DAF9", 1, {
10407     duration: 1 //duration of entire animation (not each individual ripple)
10408     // Note: Easing is not configurable and will be ignored if included
10409 });
10410 </code></pre>
10411     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10412     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10413     * @param {Object} options (optional) Object literal with any of the Fx config options
10414     * @return {Roo.Element} The Element
10415     */
10416     frame : function(color, count, o){
10417         var el = this.getFxEl();
10418         o = o || {};
10419
10420         el.queueFx(o, function(){
10421             color = color || "#C3DAF9";
10422             if(color.length == 6){
10423                 color = "#" + color;
10424             }
10425             count = count || 1;
10426             duration = o.duration || 1;
10427             this.show();
10428
10429             var b = this.getBox();
10430             var animFn = function(){
10431                 var proxy = this.createProxy({
10432
10433                      style:{
10434                         visbility:"hidden",
10435                         position:"absolute",
10436                         "z-index":"35000", // yee haw
10437                         border:"0px solid " + color
10438                      }
10439                   });
10440                 var scale = Roo.isBorderBox ? 2 : 1;
10441                 proxy.animate({
10442                     top:{from:b.y, to:b.y - 20},
10443                     left:{from:b.x, to:b.x - 20},
10444                     borderWidth:{from:0, to:10},
10445                     opacity:{from:1, to:0},
10446                     height:{from:b.height, to:(b.height + (20*scale))},
10447                     width:{from:b.width, to:(b.width + (20*scale))}
10448                 }, duration, function(){
10449                     proxy.remove();
10450                 });
10451                 if(--count > 0){
10452                      animFn.defer((duration/2)*1000, this);
10453                 }else{
10454                     el.afterFx(o);
10455                 }
10456             };
10457             animFn.call(this);
10458         });
10459         return this;
10460     },
10461
10462    /**
10463     * Creates a pause before any subsequent queued effects begin.  If there are
10464     * no effects queued after the pause it will have no effect.
10465     * Usage:
10466 <pre><code>
10467 el.pause(1);
10468 </code></pre>
10469     * @param {Number} seconds The length of time to pause (in seconds)
10470     * @return {Roo.Element} The Element
10471     */
10472     pause : function(seconds){
10473         var el = this.getFxEl();
10474         var o = {};
10475
10476         el.queueFx(o, function(){
10477             setTimeout(function(){
10478                 el.afterFx(o);
10479             }, seconds * 1000);
10480         });
10481         return this;
10482     },
10483
10484    /**
10485     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10486     * using the "endOpacity" config option.
10487     * Usage:
10488 <pre><code>
10489 // default: fade in from opacity 0 to 100%
10490 el.fadeIn();
10491
10492 // custom: fade in from opacity 0 to 75% over 2 seconds
10493 el.fadeIn({ endOpacity: .75, duration: 2});
10494
10495 // common config options shown with default values
10496 el.fadeIn({
10497     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10498     easing: 'easeOut',
10499     duration: .5
10500 });
10501 </code></pre>
10502     * @param {Object} options (optional) Object literal with any of the Fx config options
10503     * @return {Roo.Element} The Element
10504     */
10505     fadeIn : function(o){
10506         var el = this.getFxEl();
10507         o = o || {};
10508         el.queueFx(o, function(){
10509             this.setOpacity(0);
10510             this.fixDisplay();
10511             this.dom.style.visibility = 'visible';
10512             var to = o.endOpacity || 1;
10513             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10514                 o, null, .5, "easeOut", function(){
10515                 if(to == 1){
10516                     this.clearOpacity();
10517                 }
10518                 el.afterFx(o);
10519             });
10520         });
10521         return this;
10522     },
10523
10524    /**
10525     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10526     * using the "endOpacity" config option.
10527     * Usage:
10528 <pre><code>
10529 // default: fade out from the element's current opacity to 0
10530 el.fadeOut();
10531
10532 // custom: fade out from the element's current opacity to 25% over 2 seconds
10533 el.fadeOut({ endOpacity: .25, duration: 2});
10534
10535 // common config options shown with default values
10536 el.fadeOut({
10537     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10538     easing: 'easeOut',
10539     duration: .5
10540     remove: false,
10541     useDisplay: false
10542 });
10543 </code></pre>
10544     * @param {Object} options (optional) Object literal with any of the Fx config options
10545     * @return {Roo.Element} The Element
10546     */
10547     fadeOut : function(o){
10548         var el = this.getFxEl();
10549         o = o || {};
10550         el.queueFx(o, function(){
10551             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10552                 o, null, .5, "easeOut", function(){
10553                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10554                      this.dom.style.display = "none";
10555                 }else{
10556                      this.dom.style.visibility = "hidden";
10557                 }
10558                 this.clearOpacity();
10559                 el.afterFx(o);
10560             });
10561         });
10562         return this;
10563     },
10564
10565    /**
10566     * Animates the transition of an element's dimensions from a starting height/width
10567     * to an ending height/width.
10568     * Usage:
10569 <pre><code>
10570 // change height and width to 100x100 pixels
10571 el.scale(100, 100);
10572
10573 // common config options shown with default values.  The height and width will default to
10574 // the element's existing values if passed as null.
10575 el.scale(
10576     [element's width],
10577     [element's height], {
10578     easing: 'easeOut',
10579     duration: .35
10580 });
10581 </code></pre>
10582     * @param {Number} width  The new width (pass undefined to keep the original width)
10583     * @param {Number} height  The new height (pass undefined to keep the original height)
10584     * @param {Object} options (optional) Object literal with any of the Fx config options
10585     * @return {Roo.Element} The Element
10586     */
10587     scale : function(w, h, o){
10588         this.shift(Roo.apply({}, o, {
10589             width: w,
10590             height: h
10591         }));
10592         return this;
10593     },
10594
10595    /**
10596     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10597     * Any of these properties not specified in the config object will not be changed.  This effect 
10598     * requires that at least one new dimension, position or opacity setting must be passed in on
10599     * the config object in order for the function to have any effect.
10600     * Usage:
10601 <pre><code>
10602 // slide the element horizontally to x position 200 while changing the height and opacity
10603 el.shift({ x: 200, height: 50, opacity: .8 });
10604
10605 // common config options shown with default values.
10606 el.shift({
10607     width: [element's width],
10608     height: [element's height],
10609     x: [element's x position],
10610     y: [element's y position],
10611     opacity: [element's opacity],
10612     easing: 'easeOut',
10613     duration: .35
10614 });
10615 </code></pre>
10616     * @param {Object} options  Object literal with any of the Fx config options
10617     * @return {Roo.Element} The Element
10618     */
10619     shift : function(o){
10620         var el = this.getFxEl();
10621         o = o || {};
10622         el.queueFx(o, function(){
10623             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10624             if(w !== undefined){
10625                 a.width = {to: this.adjustWidth(w)};
10626             }
10627             if(h !== undefined){
10628                 a.height = {to: this.adjustHeight(h)};
10629             }
10630             if(x !== undefined || y !== undefined){
10631                 a.points = {to: [
10632                     x !== undefined ? x : this.getX(),
10633                     y !== undefined ? y : this.getY()
10634                 ]};
10635             }
10636             if(op !== undefined){
10637                 a.opacity = {to: op};
10638             }
10639             if(o.xy !== undefined){
10640                 a.points = {to: o.xy};
10641             }
10642             arguments.callee.anim = this.fxanim(a,
10643                 o, 'motion', .35, "easeOut", function(){
10644                 el.afterFx(o);
10645             });
10646         });
10647         return this;
10648     },
10649
10650         /**
10651          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10652          * ending point of the effect.
10653          * Usage:
10654          *<pre><code>
10655 // default: slide the element downward while fading out
10656 el.ghost();
10657
10658 // custom: slide the element out to the right with a 2-second duration
10659 el.ghost('r', { duration: 2 });
10660
10661 // common config options shown with default values
10662 el.ghost('b', {
10663     easing: 'easeOut',
10664     duration: .5
10665     remove: false,
10666     useDisplay: false
10667 });
10668 </code></pre>
10669          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10670          * @param {Object} options (optional) Object literal with any of the Fx config options
10671          * @return {Roo.Element} The Element
10672          */
10673     ghost : function(anchor, o){
10674         var el = this.getFxEl();
10675         o = o || {};
10676
10677         el.queueFx(o, function(){
10678             anchor = anchor || "b";
10679
10680             // restore values after effect
10681             var r = this.getFxRestore();
10682             var w = this.getWidth(),
10683                 h = this.getHeight();
10684
10685             var st = this.dom.style;
10686
10687             var after = function(){
10688                 if(o.useDisplay){
10689                     el.setDisplayed(false);
10690                 }else{
10691                     el.hide();
10692                 }
10693
10694                 el.clearOpacity();
10695                 el.setPositioning(r.pos);
10696                 st.width = r.width;
10697                 st.height = r.height;
10698
10699                 el.afterFx(o);
10700             };
10701
10702             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10703             switch(anchor.toLowerCase()){
10704                 case "t":
10705                     pt.by = [0, -h];
10706                 break;
10707                 case "l":
10708                     pt.by = [-w, 0];
10709                 break;
10710                 case "r":
10711                     pt.by = [w, 0];
10712                 break;
10713                 case "b":
10714                     pt.by = [0, h];
10715                 break;
10716                 case "tl":
10717                     pt.by = [-w, -h];
10718                 break;
10719                 case "bl":
10720                     pt.by = [-w, h];
10721                 break;
10722                 case "br":
10723                     pt.by = [w, h];
10724                 break;
10725                 case "tr":
10726                     pt.by = [w, -h];
10727                 break;
10728             }
10729
10730             arguments.callee.anim = this.fxanim(a,
10731                 o,
10732                 'motion',
10733                 .5,
10734                 "easeOut", after);
10735         });
10736         return this;
10737     },
10738
10739         /**
10740          * Ensures that all effects queued after syncFx is called on the element are
10741          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10742          * @return {Roo.Element} The Element
10743          */
10744     syncFx : function(){
10745         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10746             block : false,
10747             concurrent : true,
10748             stopFx : false
10749         });
10750         return this;
10751     },
10752
10753         /**
10754          * Ensures that all effects queued after sequenceFx is called on the element are
10755          * run in sequence.  This is the opposite of {@link #syncFx}.
10756          * @return {Roo.Element} The Element
10757          */
10758     sequenceFx : function(){
10759         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10760             block : false,
10761             concurrent : false,
10762             stopFx : false
10763         });
10764         return this;
10765     },
10766
10767         /* @private */
10768     nextFx : function(){
10769         var ef = this.fxQueue[0];
10770         if(ef){
10771             ef.call(this);
10772         }
10773     },
10774
10775         /**
10776          * Returns true if the element has any effects actively running or queued, else returns false.
10777          * @return {Boolean} True if element has active effects, else false
10778          */
10779     hasActiveFx : function(){
10780         return this.fxQueue && this.fxQueue[0];
10781     },
10782
10783         /**
10784          * Stops any running effects and clears the element's internal effects queue if it contains
10785          * any additional effects that haven't started yet.
10786          * @return {Roo.Element} The Element
10787          */
10788     stopFx : function(){
10789         if(this.hasActiveFx()){
10790             var cur = this.fxQueue[0];
10791             if(cur && cur.anim && cur.anim.isAnimated()){
10792                 this.fxQueue = [cur]; // clear out others
10793                 cur.anim.stop(true);
10794             }
10795         }
10796         return this;
10797     },
10798
10799         /* @private */
10800     beforeFx : function(o){
10801         if(this.hasActiveFx() && !o.concurrent){
10802            if(o.stopFx){
10803                this.stopFx();
10804                return true;
10805            }
10806            return false;
10807         }
10808         return true;
10809     },
10810
10811         /**
10812          * Returns true if the element is currently blocking so that no other effect can be queued
10813          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10814          * used to ensure that an effect initiated by a user action runs to completion prior to the
10815          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10816          * @return {Boolean} True if blocking, else false
10817          */
10818     hasFxBlock : function(){
10819         var q = this.fxQueue;
10820         return q && q[0] && q[0].block;
10821     },
10822
10823         /* @private */
10824     queueFx : function(o, fn){
10825         if(!this.fxQueue){
10826             this.fxQueue = [];
10827         }
10828         if(!this.hasFxBlock()){
10829             Roo.applyIf(o, this.fxDefaults);
10830             if(!o.concurrent){
10831                 var run = this.beforeFx(o);
10832                 fn.block = o.block;
10833                 this.fxQueue.push(fn);
10834                 if(run){
10835                     this.nextFx();
10836                 }
10837             }else{
10838                 fn.call(this);
10839             }
10840         }
10841         return this;
10842     },
10843
10844         /* @private */
10845     fxWrap : function(pos, o, vis){
10846         var wrap;
10847         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10848             var wrapXY;
10849             if(o.fixPosition){
10850                 wrapXY = this.getXY();
10851             }
10852             var div = document.createElement("div");
10853             div.style.visibility = vis;
10854             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10855             wrap.setPositioning(pos);
10856             if(wrap.getStyle("position") == "static"){
10857                 wrap.position("relative");
10858             }
10859             this.clearPositioning('auto');
10860             wrap.clip();
10861             wrap.dom.appendChild(this.dom);
10862             if(wrapXY){
10863                 wrap.setXY(wrapXY);
10864             }
10865         }
10866         return wrap;
10867     },
10868
10869         /* @private */
10870     fxUnwrap : function(wrap, pos, o){
10871         this.clearPositioning();
10872         this.setPositioning(pos);
10873         if(!o.wrap){
10874             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10875             wrap.remove();
10876         }
10877     },
10878
10879         /* @private */
10880     getFxRestore : function(){
10881         var st = this.dom.style;
10882         return {pos: this.getPositioning(), width: st.width, height : st.height};
10883     },
10884
10885         /* @private */
10886     afterFx : function(o){
10887         if(o.afterStyle){
10888             this.applyStyles(o.afterStyle);
10889         }
10890         if(o.afterCls){
10891             this.addClass(o.afterCls);
10892         }
10893         if(o.remove === true){
10894             this.remove();
10895         }
10896         Roo.callback(o.callback, o.scope, [this]);
10897         if(!o.concurrent){
10898             this.fxQueue.shift();
10899             this.nextFx();
10900         }
10901     },
10902
10903         /* @private */
10904     getFxEl : function(){ // support for composite element fx
10905         return Roo.get(this.dom);
10906     },
10907
10908         /* @private */
10909     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10910         animType = animType || 'run';
10911         opt = opt || {};
10912         var anim = Roo.lib.Anim[animType](
10913             this.dom, args,
10914             (opt.duration || defaultDur) || .35,
10915             (opt.easing || defaultEase) || 'easeOut',
10916             function(){
10917                 Roo.callback(cb, this);
10918             },
10919             this
10920         );
10921         opt.anim = anim;
10922         return anim;
10923     }
10924 };
10925
10926 // backwords compat
10927 Roo.Fx.resize = Roo.Fx.scale;
10928
10929 //When included, Roo.Fx is automatically applied to Element so that all basic
10930 //effects are available directly via the Element API
10931 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10932  * Based on:
10933  * Ext JS Library 1.1.1
10934  * Copyright(c) 2006-2007, Ext JS, LLC.
10935  *
10936  * Originally Released Under LGPL - original licence link has changed is not relivant.
10937  *
10938  * Fork - LGPL
10939  * <script type="text/javascript">
10940  */
10941
10942
10943 /**
10944  * @class Roo.CompositeElement
10945  * Standard composite class. Creates a Roo.Element for every element in the collection.
10946  * <br><br>
10947  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10948  * actions will be performed on all the elements in this collection.</b>
10949  * <br><br>
10950  * All methods return <i>this</i> and can be chained.
10951  <pre><code>
10952  var els = Roo.select("#some-el div.some-class", true);
10953  // or select directly from an existing element
10954  var el = Roo.get('some-el');
10955  el.select('div.some-class', true);
10956
10957  els.setWidth(100); // all elements become 100 width
10958  els.hide(true); // all elements fade out and hide
10959  // or
10960  els.setWidth(100).hide(true);
10961  </code></pre>
10962  */
10963 Roo.CompositeElement = function(els){
10964     this.elements = [];
10965     this.addElements(els);
10966 };
10967 Roo.CompositeElement.prototype = {
10968     isComposite: true,
10969     addElements : function(els){
10970         if(!els) return this;
10971         if(typeof els == "string"){
10972             els = Roo.Element.selectorFunction(els);
10973         }
10974         var yels = this.elements;
10975         var index = yels.length-1;
10976         for(var i = 0, len = els.length; i < len; i++) {
10977                 yels[++index] = Roo.get(els[i]);
10978         }
10979         return this;
10980     },
10981
10982     /**
10983     * Clears this composite and adds the elements returned by the passed selector.
10984     * @param {String/Array} els A string CSS selector, an array of elements or an element
10985     * @return {CompositeElement} this
10986     */
10987     fill : function(els){
10988         this.elements = [];
10989         this.add(els);
10990         return this;
10991     },
10992
10993     /**
10994     * Filters this composite to only elements that match the passed selector.
10995     * @param {String} selector A string CSS selector
10996     * @param {Boolean} inverse return inverse filter (not matches)
10997     * @return {CompositeElement} this
10998     */
10999     filter : function(selector, inverse){
11000         var els = [];
11001         inverse = inverse || false;
11002         this.each(function(el){
11003             var match = inverse ? !el.is(selector) : el.is(selector);
11004             if(match){
11005                 els[els.length] = el.dom;
11006             }
11007         });
11008         this.fill(els);
11009         return this;
11010     },
11011
11012     invoke : function(fn, args){
11013         var els = this.elements;
11014         for(var i = 0, len = els.length; i < len; i++) {
11015                 Roo.Element.prototype[fn].apply(els[i], args);
11016         }
11017         return this;
11018     },
11019     /**
11020     * Adds elements to this composite.
11021     * @param {String/Array} els A string CSS selector, an array of elements or an element
11022     * @return {CompositeElement} this
11023     */
11024     add : function(els){
11025         if(typeof els == "string"){
11026             this.addElements(Roo.Element.selectorFunction(els));
11027         }else if(els.length !== undefined){
11028             this.addElements(els);
11029         }else{
11030             this.addElements([els]);
11031         }
11032         return this;
11033     },
11034     /**
11035     * Calls the passed function passing (el, this, index) for each element in this composite.
11036     * @param {Function} fn The function to call
11037     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11038     * @return {CompositeElement} this
11039     */
11040     each : function(fn, scope){
11041         var els = this.elements;
11042         for(var i = 0, len = els.length; i < len; i++){
11043             if(fn.call(scope || els[i], els[i], this, i) === false) {
11044                 break;
11045             }
11046         }
11047         return this;
11048     },
11049
11050     /**
11051      * Returns the Element object at the specified index
11052      * @param {Number} index
11053      * @return {Roo.Element}
11054      */
11055     item : function(index){
11056         return this.elements[index] || null;
11057     },
11058
11059     /**
11060      * Returns the first Element
11061      * @return {Roo.Element}
11062      */
11063     first : function(){
11064         return this.item(0);
11065     },
11066
11067     /**
11068      * Returns the last Element
11069      * @return {Roo.Element}
11070      */
11071     last : function(){
11072         return this.item(this.elements.length-1);
11073     },
11074
11075     /**
11076      * Returns the number of elements in this composite
11077      * @return Number
11078      */
11079     getCount : function(){
11080         return this.elements.length;
11081     },
11082
11083     /**
11084      * Returns true if this composite contains the passed element
11085      * @return Boolean
11086      */
11087     contains : function(el){
11088         return this.indexOf(el) !== -1;
11089     },
11090
11091     /**
11092      * Returns true if this composite contains the passed element
11093      * @return Boolean
11094      */
11095     indexOf : function(el){
11096         return this.elements.indexOf(Roo.get(el));
11097     },
11098
11099
11100     /**
11101     * Removes the specified element(s).
11102     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11103     * or an array of any of those.
11104     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11105     * @return {CompositeElement} this
11106     */
11107     removeElement : function(el, removeDom){
11108         if(el instanceof Array){
11109             for(var i = 0, len = el.length; i < len; i++){
11110                 this.removeElement(el[i]);
11111             }
11112             return this;
11113         }
11114         var index = typeof el == 'number' ? el : this.indexOf(el);
11115         if(index !== -1){
11116             if(removeDom){
11117                 var d = this.elements[index];
11118                 if(d.dom){
11119                     d.remove();
11120                 }else{
11121                     d.parentNode.removeChild(d);
11122                 }
11123             }
11124             this.elements.splice(index, 1);
11125         }
11126         return this;
11127     },
11128
11129     /**
11130     * Replaces the specified element with the passed element.
11131     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11132     * to replace.
11133     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11134     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11135     * @return {CompositeElement} this
11136     */
11137     replaceElement : function(el, replacement, domReplace){
11138         var index = typeof el == 'number' ? el : this.indexOf(el);
11139         if(index !== -1){
11140             if(domReplace){
11141                 this.elements[index].replaceWith(replacement);
11142             }else{
11143                 this.elements.splice(index, 1, Roo.get(replacement))
11144             }
11145         }
11146         return this;
11147     },
11148
11149     /**
11150      * Removes all elements.
11151      */
11152     clear : function(){
11153         this.elements = [];
11154     }
11155 };
11156 (function(){
11157     Roo.CompositeElement.createCall = function(proto, fnName){
11158         if(!proto[fnName]){
11159             proto[fnName] = function(){
11160                 return this.invoke(fnName, arguments);
11161             };
11162         }
11163     };
11164     for(var fnName in Roo.Element.prototype){
11165         if(typeof Roo.Element.prototype[fnName] == "function"){
11166             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11167         }
11168     };
11169 })();
11170 /*
11171  * Based on:
11172  * Ext JS Library 1.1.1
11173  * Copyright(c) 2006-2007, Ext JS, LLC.
11174  *
11175  * Originally Released Under LGPL - original licence link has changed is not relivant.
11176  *
11177  * Fork - LGPL
11178  * <script type="text/javascript">
11179  */
11180
11181 /**
11182  * @class Roo.CompositeElementLite
11183  * @extends Roo.CompositeElement
11184  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11185  <pre><code>
11186  var els = Roo.select("#some-el div.some-class");
11187  // or select directly from an existing element
11188  var el = Roo.get('some-el');
11189  el.select('div.some-class');
11190
11191  els.setWidth(100); // all elements become 100 width
11192  els.hide(true); // all elements fade out and hide
11193  // or
11194  els.setWidth(100).hide(true);
11195  </code></pre><br><br>
11196  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11197  * actions will be performed on all the elements in this collection.</b>
11198  */
11199 Roo.CompositeElementLite = function(els){
11200     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11201     this.el = new Roo.Element.Flyweight();
11202 };
11203 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11204     addElements : function(els){
11205         if(els){
11206             if(els instanceof Array){
11207                 this.elements = this.elements.concat(els);
11208             }else{
11209                 var yels = this.elements;
11210                 var index = yels.length-1;
11211                 for(var i = 0, len = els.length; i < len; i++) {
11212                     yels[++index] = els[i];
11213                 }
11214             }
11215         }
11216         return this;
11217     },
11218     invoke : function(fn, args){
11219         var els = this.elements;
11220         var el = this.el;
11221         for(var i = 0, len = els.length; i < len; i++) {
11222             el.dom = els[i];
11223                 Roo.Element.prototype[fn].apply(el, args);
11224         }
11225         return this;
11226     },
11227     /**
11228      * Returns a flyweight Element of the dom element object at the specified index
11229      * @param {Number} index
11230      * @return {Roo.Element}
11231      */
11232     item : function(index){
11233         if(!this.elements[index]){
11234             return null;
11235         }
11236         this.el.dom = this.elements[index];
11237         return this.el;
11238     },
11239
11240     // fixes scope with flyweight
11241     addListener : function(eventName, handler, scope, opt){
11242         var els = this.elements;
11243         for(var i = 0, len = els.length; i < len; i++) {
11244             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11245         }
11246         return this;
11247     },
11248
11249     /**
11250     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11251     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11252     * a reference to the dom node, use el.dom.</b>
11253     * @param {Function} fn The function to call
11254     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11255     * @return {CompositeElement} this
11256     */
11257     each : function(fn, scope){
11258         var els = this.elements;
11259         var el = this.el;
11260         for(var i = 0, len = els.length; i < len; i++){
11261             el.dom = els[i];
11262                 if(fn.call(scope || el, el, this, i) === false){
11263                 break;
11264             }
11265         }
11266         return this;
11267     },
11268
11269     indexOf : function(el){
11270         return this.elements.indexOf(Roo.getDom(el));
11271     },
11272
11273     replaceElement : function(el, replacement, domReplace){
11274         var index = typeof el == 'number' ? el : this.indexOf(el);
11275         if(index !== -1){
11276             replacement = Roo.getDom(replacement);
11277             if(domReplace){
11278                 var d = this.elements[index];
11279                 d.parentNode.insertBefore(replacement, d);
11280                 d.parentNode.removeChild(d);
11281             }
11282             this.elements.splice(index, 1, replacement);
11283         }
11284         return this;
11285     }
11286 });
11287 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11288
11289 /*
11290  * Based on:
11291  * Ext JS Library 1.1.1
11292  * Copyright(c) 2006-2007, Ext JS, LLC.
11293  *
11294  * Originally Released Under LGPL - original licence link has changed is not relivant.
11295  *
11296  * Fork - LGPL
11297  * <script type="text/javascript">
11298  */
11299
11300  
11301
11302 /**
11303  * @class Roo.data.Connection
11304  * @extends Roo.util.Observable
11305  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11306  * either to a configured URL, or to a URL specified at request time.<br><br>
11307  * <p>
11308  * Requests made by this class are asynchronous, and will return immediately. No data from
11309  * the server will be available to the statement immediately following the {@link #request} call.
11310  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11311  * <p>
11312  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11313  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11314  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11315  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11316  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11317  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11318  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11319  * standard DOM methods.
11320  * @constructor
11321  * @param {Object} config a configuration object.
11322  */
11323 Roo.data.Connection = function(config){
11324     Roo.apply(this, config);
11325     this.addEvents({
11326         /**
11327          * @event beforerequest
11328          * Fires before a network request is made to retrieve a data object.
11329          * @param {Connection} conn This Connection object.
11330          * @param {Object} options The options config object passed to the {@link #request} method.
11331          */
11332         "beforerequest" : true,
11333         /**
11334          * @event requestcomplete
11335          * Fires if the request was successfully completed.
11336          * @param {Connection} conn This Connection object.
11337          * @param {Object} response The XHR object containing the response data.
11338          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11339          * @param {Object} options The options config object passed to the {@link #request} method.
11340          */
11341         "requestcomplete" : true,
11342         /**
11343          * @event requestexception
11344          * Fires if an error HTTP status was returned from the server.
11345          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11346          * @param {Connection} conn This Connection object.
11347          * @param {Object} response The XHR object containing the response data.
11348          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11349          * @param {Object} options The options config object passed to the {@link #request} method.
11350          */
11351         "requestexception" : true
11352     });
11353     Roo.data.Connection.superclass.constructor.call(this);
11354 };
11355
11356 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11357     /**
11358      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11359      */
11360     /**
11361      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11362      * extra parameters to each request made by this object. (defaults to undefined)
11363      */
11364     /**
11365      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11366      *  to each request made by this object. (defaults to undefined)
11367      */
11368     /**
11369      * @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)
11370      */
11371     /**
11372      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11373      */
11374     timeout : 30000,
11375     /**
11376      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11377      * @type Boolean
11378      */
11379     autoAbort:false,
11380
11381     /**
11382      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11383      * @type Boolean
11384      */
11385     disableCaching: true,
11386
11387     /**
11388      * Sends an HTTP request to a remote server.
11389      * @param {Object} options An object which may contain the following properties:<ul>
11390      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11391      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11392      * request, a url encoded string or a function to call to get either.</li>
11393      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11394      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11395      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11396      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11397      * <li>options {Object} The parameter to the request call.</li>
11398      * <li>success {Boolean} True if the request succeeded.</li>
11399      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11400      * </ul></li>
11401      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11402      * The callback is passed the following parameters:<ul>
11403      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11404      * <li>options {Object} The parameter to the request call.</li>
11405      * </ul></li>
11406      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11407      * The callback is passed the following parameters:<ul>
11408      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11409      * <li>options {Object} The parameter to the request call.</li>
11410      * </ul></li>
11411      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11412      * for the callback function. Defaults to the browser window.</li>
11413      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11414      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11415      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11416      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11417      * params for the post data. Any params will be appended to the URL.</li>
11418      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11419      * </ul>
11420      * @return {Number} transactionId
11421      */
11422     request : function(o){
11423         if(this.fireEvent("beforerequest", this, o) !== false){
11424             var p = o.params;
11425
11426             if(typeof p == "function"){
11427                 p = p.call(o.scope||window, o);
11428             }
11429             if(typeof p == "object"){
11430                 p = Roo.urlEncode(o.params);
11431             }
11432             if(this.extraParams){
11433                 var extras = Roo.urlEncode(this.extraParams);
11434                 p = p ? (p + '&' + extras) : extras;
11435             }
11436
11437             var url = o.url || this.url;
11438             if(typeof url == 'function'){
11439                 url = url.call(o.scope||window, o);
11440             }
11441
11442             if(o.form){
11443                 var form = Roo.getDom(o.form);
11444                 url = url || form.action;
11445
11446                 var enctype = form.getAttribute("enctype");
11447                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11448                     return this.doFormUpload(o, p, url);
11449                 }
11450                 var f = Roo.lib.Ajax.serializeForm(form);
11451                 p = p ? (p + '&' + f) : f;
11452             }
11453
11454             var hs = o.headers;
11455             if(this.defaultHeaders){
11456                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11457                 if(!o.headers){
11458                     o.headers = hs;
11459                 }
11460             }
11461
11462             var cb = {
11463                 success: this.handleResponse,
11464                 failure: this.handleFailure,
11465                 scope: this,
11466                 argument: {options: o},
11467                 timeout : o.timeout || this.timeout
11468             };
11469
11470             var method = o.method||this.method||(p ? "POST" : "GET");
11471
11472             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11473                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11474             }
11475
11476             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11477                 if(o.autoAbort){
11478                     this.abort();
11479                 }
11480             }else if(this.autoAbort !== false){
11481                 this.abort();
11482             }
11483
11484             if((method == 'GET' && p) || o.xmlData){
11485                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11486                 p = '';
11487             }
11488             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11489             return this.transId;
11490         }else{
11491             Roo.callback(o.callback, o.scope, [o, null, null]);
11492             return null;
11493         }
11494     },
11495
11496     /**
11497      * Determine whether this object has a request outstanding.
11498      * @param {Number} transactionId (Optional) defaults to the last transaction
11499      * @return {Boolean} True if there is an outstanding request.
11500      */
11501     isLoading : function(transId){
11502         if(transId){
11503             return Roo.lib.Ajax.isCallInProgress(transId);
11504         }else{
11505             return this.transId ? true : false;
11506         }
11507     },
11508
11509     /**
11510      * Aborts any outstanding request.
11511      * @param {Number} transactionId (Optional) defaults to the last transaction
11512      */
11513     abort : function(transId){
11514         if(transId || this.isLoading()){
11515             Roo.lib.Ajax.abort(transId || this.transId);
11516         }
11517     },
11518
11519     // private
11520     handleResponse : function(response){
11521         this.transId = false;
11522         var options = response.argument.options;
11523         response.argument = options ? options.argument : null;
11524         this.fireEvent("requestcomplete", this, response, options);
11525         Roo.callback(options.success, options.scope, [response, options]);
11526         Roo.callback(options.callback, options.scope, [options, true, response]);
11527     },
11528
11529     // private
11530     handleFailure : function(response, e){
11531         this.transId = false;
11532         var options = response.argument.options;
11533         response.argument = options ? options.argument : null;
11534         this.fireEvent("requestexception", this, response, options, e);
11535         Roo.callback(options.failure, options.scope, [response, options]);
11536         Roo.callback(options.callback, options.scope, [options, false, response]);
11537     },
11538
11539     // private
11540     doFormUpload : function(o, ps, url){
11541         var id = Roo.id();
11542         var frame = document.createElement('iframe');
11543         frame.id = id;
11544         frame.name = id;
11545         frame.className = 'x-hidden';
11546         if(Roo.isIE){
11547             frame.src = Roo.SSL_SECURE_URL;
11548         }
11549         document.body.appendChild(frame);
11550
11551         if(Roo.isIE){
11552            document.frames[id].name = id;
11553         }
11554
11555         var form = Roo.getDom(o.form);
11556         form.target = id;
11557         form.method = 'POST';
11558         form.enctype = form.encoding = 'multipart/form-data';
11559         if(url){
11560             form.action = url;
11561         }
11562
11563         var hiddens, hd;
11564         if(ps){ // add dynamic params
11565             hiddens = [];
11566             ps = Roo.urlDecode(ps, false);
11567             for(var k in ps){
11568                 if(ps.hasOwnProperty(k)){
11569                     hd = document.createElement('input');
11570                     hd.type = 'hidden';
11571                     hd.name = k;
11572                     hd.value = ps[k];
11573                     form.appendChild(hd);
11574                     hiddens.push(hd);
11575                 }
11576             }
11577         }
11578
11579         function cb(){
11580             var r = {  // bogus response object
11581                 responseText : '',
11582                 responseXML : null
11583             };
11584
11585             r.argument = o ? o.argument : null;
11586
11587             try { //
11588                 var doc;
11589                 if(Roo.isIE){
11590                     doc = frame.contentWindow.document;
11591                 }else {
11592                     doc = (frame.contentDocument || window.frames[id].document);
11593                 }
11594                 if(doc && doc.body){
11595                     r.responseText = doc.body.innerHTML;
11596                 }
11597                 if(doc && doc.XMLDocument){
11598                     r.responseXML = doc.XMLDocument;
11599                 }else {
11600                     r.responseXML = doc;
11601                 }
11602             }
11603             catch(e) {
11604                 // ignore
11605             }
11606
11607             Roo.EventManager.removeListener(frame, 'load', cb, this);
11608
11609             this.fireEvent("requestcomplete", this, r, o);
11610             Roo.callback(o.success, o.scope, [r, o]);
11611             Roo.callback(o.callback, o.scope, [o, true, r]);
11612
11613             setTimeout(function(){document.body.removeChild(frame);}, 100);
11614         }
11615
11616         Roo.EventManager.on(frame, 'load', cb, this);
11617         form.submit();
11618
11619         if(hiddens){ // remove dynamic params
11620             for(var i = 0, len = hiddens.length; i < len; i++){
11621                 form.removeChild(hiddens[i]);
11622             }
11623         }
11624     }
11625 });
11626 /*
11627  * Based on:
11628  * Ext JS Library 1.1.1
11629  * Copyright(c) 2006-2007, Ext JS, LLC.
11630  *
11631  * Originally Released Under LGPL - original licence link has changed is not relivant.
11632  *
11633  * Fork - LGPL
11634  * <script type="text/javascript">
11635  */
11636  
11637 /**
11638  * Global Ajax request class.
11639  * 
11640  * @class Roo.Ajax
11641  * @extends Roo.data.Connection
11642  * @static
11643  * 
11644  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11645  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11646  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11647  * @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)
11648  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11649  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11650  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11651  */
11652 Roo.Ajax = new Roo.data.Connection({
11653     // fix up the docs
11654     /**
11655      * @scope Roo.Ajax
11656      * @type {Boolear} 
11657      */
11658     autoAbort : false,
11659
11660     /**
11661      * Serialize the passed form into a url encoded string
11662      * @scope Roo.Ajax
11663      * @param {String/HTMLElement} form
11664      * @return {String}
11665      */
11666     serializeForm : function(form){
11667         return Roo.lib.Ajax.serializeForm(form);
11668     }
11669 });/*
11670  * Based on:
11671  * Ext JS Library 1.1.1
11672  * Copyright(c) 2006-2007, Ext JS, LLC.
11673  *
11674  * Originally Released Under LGPL - original licence link has changed is not relivant.
11675  *
11676  * Fork - LGPL
11677  * <script type="text/javascript">
11678  */
11679
11680  
11681 /**
11682  * @class Roo.UpdateManager
11683  * @extends Roo.util.Observable
11684  * Provides AJAX-style update for Element object.<br><br>
11685  * Usage:<br>
11686  * <pre><code>
11687  * // Get it from a Roo.Element object
11688  * var el = Roo.get("foo");
11689  * var mgr = el.getUpdateManager();
11690  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11691  * ...
11692  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11693  * <br>
11694  * // or directly (returns the same UpdateManager instance)
11695  * var mgr = new Roo.UpdateManager("myElementId");
11696  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11697  * mgr.on("update", myFcnNeedsToKnow);
11698  * <br>
11699    // short handed call directly from the element object
11700    Roo.get("foo").load({
11701         url: "bar.php",
11702         scripts:true,
11703         params: "for=bar",
11704         text: "Loading Foo..."
11705    });
11706  * </code></pre>
11707  * @constructor
11708  * Create new UpdateManager directly.
11709  * @param {String/HTMLElement/Roo.Element} el The element to update
11710  * @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).
11711  */
11712 Roo.UpdateManager = function(el, forceNew){
11713     el = Roo.get(el);
11714     if(!forceNew && el.updateManager){
11715         return el.updateManager;
11716     }
11717     /**
11718      * The Element object
11719      * @type Roo.Element
11720      */
11721     this.el = el;
11722     /**
11723      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11724      * @type String
11725      */
11726     this.defaultUrl = null;
11727
11728     this.addEvents({
11729         /**
11730          * @event beforeupdate
11731          * Fired before an update is made, return false from your handler and the update is cancelled.
11732          * @param {Roo.Element} el
11733          * @param {String/Object/Function} url
11734          * @param {String/Object} params
11735          */
11736         "beforeupdate": true,
11737         /**
11738          * @event update
11739          * Fired after successful update is made.
11740          * @param {Roo.Element} el
11741          * @param {Object} oResponseObject The response Object
11742          */
11743         "update": true,
11744         /**
11745          * @event failure
11746          * Fired on update failure.
11747          * @param {Roo.Element} el
11748          * @param {Object} oResponseObject The response Object
11749          */
11750         "failure": true
11751     });
11752     var d = Roo.UpdateManager.defaults;
11753     /**
11754      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11755      * @type String
11756      */
11757     this.sslBlankUrl = d.sslBlankUrl;
11758     /**
11759      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11760      * @type Boolean
11761      */
11762     this.disableCaching = d.disableCaching;
11763     /**
11764      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11765      * @type String
11766      */
11767     this.indicatorText = d.indicatorText;
11768     /**
11769      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11770      * @type String
11771      */
11772     this.showLoadIndicator = d.showLoadIndicator;
11773     /**
11774      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11775      * @type Number
11776      */
11777     this.timeout = d.timeout;
11778
11779     /**
11780      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11781      * @type Boolean
11782      */
11783     this.loadScripts = d.loadScripts;
11784
11785     /**
11786      * Transaction object of current executing transaction
11787      */
11788     this.transaction = null;
11789
11790     /**
11791      * @private
11792      */
11793     this.autoRefreshProcId = null;
11794     /**
11795      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11796      * @type Function
11797      */
11798     this.refreshDelegate = this.refresh.createDelegate(this);
11799     /**
11800      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11801      * @type Function
11802      */
11803     this.updateDelegate = this.update.createDelegate(this);
11804     /**
11805      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11806      * @type Function
11807      */
11808     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11809     /**
11810      * @private
11811      */
11812     this.successDelegate = this.processSuccess.createDelegate(this);
11813     /**
11814      * @private
11815      */
11816     this.failureDelegate = this.processFailure.createDelegate(this);
11817
11818     if(!this.renderer){
11819      /**
11820       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11821       */
11822     this.renderer = new Roo.UpdateManager.BasicRenderer();
11823     }
11824     
11825     Roo.UpdateManager.superclass.constructor.call(this);
11826 };
11827
11828 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11829     /**
11830      * Get the Element this UpdateManager is bound to
11831      * @return {Roo.Element} The element
11832      */
11833     getEl : function(){
11834         return this.el;
11835     },
11836     /**
11837      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11838      * @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:
11839 <pre><code>
11840 um.update({<br/>
11841     url: "your-url.php",<br/>
11842     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11843     callback: yourFunction,<br/>
11844     scope: yourObject, //(optional scope)  <br/>
11845     discardUrl: false, <br/>
11846     nocache: false,<br/>
11847     text: "Loading...",<br/>
11848     timeout: 30,<br/>
11849     scripts: false<br/>
11850 });
11851 </code></pre>
11852      * The only required property is url. The optional properties nocache, text and scripts
11853      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11854      * @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}
11855      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11856      * @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.
11857      */
11858     update : function(url, params, callback, discardUrl){
11859         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11860             var method = this.method,
11861                 cfg;
11862             if(typeof url == "object"){ // must be config object
11863                 cfg = url;
11864                 url = cfg.url;
11865                 params = params || cfg.params;
11866                 callback = callback || cfg.callback;
11867                 discardUrl = discardUrl || cfg.discardUrl;
11868                 if(callback && cfg.scope){
11869                     callback = callback.createDelegate(cfg.scope);
11870                 }
11871                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11872                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11873                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11874                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11875                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11876             }
11877             this.showLoading();
11878             if(!discardUrl){
11879                 this.defaultUrl = url;
11880             }
11881             if(typeof url == "function"){
11882                 url = url.call(this);
11883             }
11884
11885             method = method || (params ? "POST" : "GET");
11886             if(method == "GET"){
11887                 url = this.prepareUrl(url);
11888             }
11889
11890             var o = Roo.apply(cfg ||{}, {
11891                 url : url,
11892                 params: params,
11893                 success: this.successDelegate,
11894                 failure: this.failureDelegate,
11895                 callback: undefined,
11896                 timeout: (this.timeout*1000),
11897                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11898             });
11899             Roo.log("updated manager called with timeout of " + o.timeout);
11900             this.transaction = Roo.Ajax.request(o);
11901         }
11902     },
11903
11904     /**
11905      * 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.
11906      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11907      * @param {String/HTMLElement} form The form Id or form element
11908      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11909      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11910      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11911      */
11912     formUpdate : function(form, url, reset, callback){
11913         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11914             if(typeof url == "function"){
11915                 url = url.call(this);
11916             }
11917             form = Roo.getDom(form);
11918             this.transaction = Roo.Ajax.request({
11919                 form: form,
11920                 url:url,
11921                 success: this.successDelegate,
11922                 failure: this.failureDelegate,
11923                 timeout: (this.timeout*1000),
11924                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11925             });
11926             this.showLoading.defer(1, this);
11927         }
11928     },
11929
11930     /**
11931      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11932      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11933      */
11934     refresh : function(callback){
11935         if(this.defaultUrl == null){
11936             return;
11937         }
11938         this.update(this.defaultUrl, null, callback, true);
11939     },
11940
11941     /**
11942      * Set this element to auto refresh.
11943      * @param {Number} interval How often to update (in seconds).
11944      * @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)
11945      * @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}
11946      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11947      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11948      */
11949     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11950         if(refreshNow){
11951             this.update(url || this.defaultUrl, params, callback, true);
11952         }
11953         if(this.autoRefreshProcId){
11954             clearInterval(this.autoRefreshProcId);
11955         }
11956         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11957     },
11958
11959     /**
11960      * Stop auto refresh on this element.
11961      */
11962      stopAutoRefresh : function(){
11963         if(this.autoRefreshProcId){
11964             clearInterval(this.autoRefreshProcId);
11965             delete this.autoRefreshProcId;
11966         }
11967     },
11968
11969     isAutoRefreshing : function(){
11970        return this.autoRefreshProcId ? true : false;
11971     },
11972     /**
11973      * Called to update the element to "Loading" state. Override to perform custom action.
11974      */
11975     showLoading : function(){
11976         if(this.showLoadIndicator){
11977             this.el.update(this.indicatorText);
11978         }
11979     },
11980
11981     /**
11982      * Adds unique parameter to query string if disableCaching = true
11983      * @private
11984      */
11985     prepareUrl : function(url){
11986         if(this.disableCaching){
11987             var append = "_dc=" + (new Date().getTime());
11988             if(url.indexOf("?") !== -1){
11989                 url += "&" + append;
11990             }else{
11991                 url += "?" + append;
11992             }
11993         }
11994         return url;
11995     },
11996
11997     /**
11998      * @private
11999      */
12000     processSuccess : function(response){
12001         this.transaction = null;
12002         if(response.argument.form && response.argument.reset){
12003             try{ // put in try/catch since some older FF releases had problems with this
12004                 response.argument.form.reset();
12005             }catch(e){}
12006         }
12007         if(this.loadScripts){
12008             this.renderer.render(this.el, response, this,
12009                 this.updateComplete.createDelegate(this, [response]));
12010         }else{
12011             this.renderer.render(this.el, response, this);
12012             this.updateComplete(response);
12013         }
12014     },
12015
12016     updateComplete : function(response){
12017         this.fireEvent("update", this.el, response);
12018         if(typeof response.argument.callback == "function"){
12019             response.argument.callback(this.el, true, response);
12020         }
12021     },
12022
12023     /**
12024      * @private
12025      */
12026     processFailure : function(response){
12027         this.transaction = null;
12028         this.fireEvent("failure", this.el, response);
12029         if(typeof response.argument.callback == "function"){
12030             response.argument.callback(this.el, false, response);
12031         }
12032     },
12033
12034     /**
12035      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12036      * @param {Object} renderer The object implementing the render() method
12037      */
12038     setRenderer : function(renderer){
12039         this.renderer = renderer;
12040     },
12041
12042     getRenderer : function(){
12043        return this.renderer;
12044     },
12045
12046     /**
12047      * Set the defaultUrl used for updates
12048      * @param {String/Function} defaultUrl The url or a function to call to get the url
12049      */
12050     setDefaultUrl : function(defaultUrl){
12051         this.defaultUrl = defaultUrl;
12052     },
12053
12054     /**
12055      * Aborts the executing transaction
12056      */
12057     abort : function(){
12058         if(this.transaction){
12059             Roo.Ajax.abort(this.transaction);
12060         }
12061     },
12062
12063     /**
12064      * Returns true if an update is in progress
12065      * @return {Boolean}
12066      */
12067     isUpdating : function(){
12068         if(this.transaction){
12069             return Roo.Ajax.isLoading(this.transaction);
12070         }
12071         return false;
12072     }
12073 });
12074
12075 /**
12076  * @class Roo.UpdateManager.defaults
12077  * @static (not really - but it helps the doc tool)
12078  * The defaults collection enables customizing the default properties of UpdateManager
12079  */
12080    Roo.UpdateManager.defaults = {
12081        /**
12082          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12083          * @type Number
12084          */
12085          timeout : 30,
12086
12087          /**
12088          * True to process scripts by default (Defaults to false).
12089          * @type Boolean
12090          */
12091         loadScripts : false,
12092
12093         /**
12094         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12095         * @type String
12096         */
12097         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12098         /**
12099          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12100          * @type Boolean
12101          */
12102         disableCaching : false,
12103         /**
12104          * Whether to show indicatorText when loading (Defaults to true).
12105          * @type Boolean
12106          */
12107         showLoadIndicator : true,
12108         /**
12109          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12110          * @type String
12111          */
12112         indicatorText : '<div class="loading-indicator">Loading...</div>'
12113    };
12114
12115 /**
12116  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12117  *Usage:
12118  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12119  * @param {String/HTMLElement/Roo.Element} el The element to update
12120  * @param {String} url The url
12121  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12122  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12123  * @static
12124  * @deprecated
12125  * @member Roo.UpdateManager
12126  */
12127 Roo.UpdateManager.updateElement = function(el, url, params, options){
12128     var um = Roo.get(el, true).getUpdateManager();
12129     Roo.apply(um, options);
12130     um.update(url, params, options ? options.callback : null);
12131 };
12132 // alias for backwards compat
12133 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12134 /**
12135  * @class Roo.UpdateManager.BasicRenderer
12136  * Default Content renderer. Updates the elements innerHTML with the responseText.
12137  */
12138 Roo.UpdateManager.BasicRenderer = function(){};
12139
12140 Roo.UpdateManager.BasicRenderer.prototype = {
12141     /**
12142      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12143      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12144      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12145      * @param {Roo.Element} el The element being rendered
12146      * @param {Object} response The YUI Connect response object
12147      * @param {UpdateManager} updateManager The calling update manager
12148      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12149      */
12150      render : function(el, response, updateManager, callback){
12151         el.update(response.responseText, updateManager.loadScripts, callback);
12152     }
12153 };
12154 /*
12155  * Based on:
12156  * Roo JS
12157  * (c)) Alan Knowles
12158  * Licence : LGPL
12159  */
12160
12161
12162 /**
12163  * @class Roo.DomTemplate
12164  * @extends Roo.Template
12165  * An effort at a dom based template engine..
12166  *
12167  * Similar to XTemplate, except it uses dom parsing to create the template..
12168  *
12169  * Supported features:
12170  *
12171  *  Tags:
12172
12173 <pre><code>
12174       {a_variable} - output encoded.
12175       {a_variable.format:("Y-m-d")} - call a method on the variable
12176       {a_variable:raw} - unencoded output
12177       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12178       {a_variable:this.method_on_template(...)} - call a method on the template object.
12179  
12180 </code></pre>
12181  *  The tpl tag:
12182 <pre><code>
12183         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12184         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12185         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12186         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12187   
12188 </code></pre>
12189  *      
12190  */
12191 Roo.DomTemplate = function()
12192 {
12193      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12194      if (this.html) {
12195         this.compile();
12196      }
12197 };
12198
12199
12200 Roo.extend(Roo.DomTemplate, Roo.Template, {
12201     /**
12202      * id counter for sub templates.
12203      */
12204     id : 0,
12205     /**
12206      * flag to indicate if dom parser is inside a pre,
12207      * it will strip whitespace if not.
12208      */
12209     inPre : false,
12210     
12211     /**
12212      * The various sub templates
12213      */
12214     tpls : false,
12215     
12216     
12217     
12218     /**
12219      *
12220      * basic tag replacing syntax
12221      * WORD:WORD()
12222      *
12223      * // you can fake an object call by doing this
12224      *  x.t:(test,tesT) 
12225      * 
12226      */
12227     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12228     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12229     
12230     iterChild : function (node, method) {
12231         
12232         var oldPre = this.inPre;
12233         if (node.tagName == 'PRE') {
12234             this.inPre = true;
12235         }
12236         for( var i = 0; i < node.childNodes.length; i++) {
12237             method.call(this, node.childNodes[i]);
12238         }
12239         this.inPre = oldPre;
12240     },
12241     
12242     
12243     
12244     /**
12245      * compile the template
12246      *
12247      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12248      *
12249      */
12250     compile: function()
12251     {
12252         var s = this.html;
12253         
12254         // covert the html into DOM...
12255         var doc = false;
12256         var div =false;
12257         try {
12258             doc = document.implementation.createHTMLDocument("");
12259             doc.documentElement.innerHTML =   this.html  ;
12260             div = doc.documentElement;
12261         } catch (e) {
12262             // old IE... - nasty -- it causes all sorts of issues.. with
12263             // images getting pulled from server..
12264             div = document.createElement('div');
12265             div.innerHTML = this.html;
12266         }
12267         //doc.documentElement.innerHTML = htmlBody
12268          
12269         
12270         
12271         this.tpls = [];
12272         var _t = this;
12273         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12274         
12275         var tpls = this.tpls;
12276         
12277         // create a top level template from the snippet..
12278         
12279         //Roo.log(div.innerHTML);
12280         
12281         var tpl = {
12282             uid : 'master',
12283             id : this.id++,
12284             attr : false,
12285             value : false,
12286             body : div.innerHTML,
12287             
12288             forCall : false,
12289             execCall : false,
12290             dom : div,
12291             isTop : true
12292             
12293         };
12294         tpls.unshift(tpl);
12295         
12296         
12297         // compile them...
12298         this.tpls = [];
12299         Roo.each(tpls, function(tp){
12300             this.compileTpl(tp);
12301             this.tpls[tp.id] = tp;
12302         }, this);
12303         
12304         this.master = tpls[0];
12305         return this;
12306         
12307         
12308     },
12309     
12310     compileNode : function(node, istop) {
12311         // test for
12312         //Roo.log(node);
12313         
12314         
12315         // skip anything not a tag..
12316         if (node.nodeType != 1) {
12317             if (node.nodeType == 3 && !this.inPre) {
12318                 // reduce white space..
12319                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12320                 
12321             }
12322             return;
12323         }
12324         
12325         var tpl = {
12326             uid : false,
12327             id : false,
12328             attr : false,
12329             value : false,
12330             body : '',
12331             
12332             forCall : false,
12333             execCall : false,
12334             dom : false,
12335             isTop : istop
12336             
12337             
12338         };
12339         
12340         
12341         switch(true) {
12342             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12343             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12344             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12345             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12346             // no default..
12347         }
12348         
12349         
12350         if (!tpl.attr) {
12351             // just itterate children..
12352             this.iterChild(node,this.compileNode);
12353             return;
12354         }
12355         tpl.uid = this.id++;
12356         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12357         node.removeAttribute('roo-'+ tpl.attr);
12358         if (tpl.attr != 'name') {
12359             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12360             node.parentNode.replaceChild(placeholder,  node);
12361         } else {
12362             
12363             var placeholder =  document.createElement('span');
12364             placeholder.className = 'roo-tpl-' + tpl.value;
12365             node.parentNode.replaceChild(placeholder,  node);
12366         }
12367         
12368         // parent now sees '{domtplXXXX}
12369         this.iterChild(node,this.compileNode);
12370         
12371         // we should now have node body...
12372         var div = document.createElement('div');
12373         div.appendChild(node);
12374         tpl.dom = node;
12375         // this has the unfortunate side effect of converting tagged attributes
12376         // eg. href="{...}" into %7C...%7D
12377         // this has been fixed by searching for those combo's although it's a bit hacky..
12378         
12379         
12380         tpl.body = div.innerHTML;
12381         
12382         
12383          
12384         tpl.id = tpl.uid;
12385         switch(tpl.attr) {
12386             case 'for' :
12387                 switch (tpl.value) {
12388                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12389                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12390                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12391                 }
12392                 break;
12393             
12394             case 'exec':
12395                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12396                 break;
12397             
12398             case 'if':     
12399                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12400                 break;
12401             
12402             case 'name':
12403                 tpl.id  = tpl.value; // replace non characters???
12404                 break;
12405             
12406         }
12407         
12408         
12409         this.tpls.push(tpl);
12410         
12411         
12412         
12413     },
12414     
12415     
12416     
12417     
12418     /**
12419      * Compile a segment of the template into a 'sub-template'
12420      *
12421      * 
12422      * 
12423      *
12424      */
12425     compileTpl : function(tpl)
12426     {
12427         var fm = Roo.util.Format;
12428         var useF = this.disableFormats !== true;
12429         
12430         var sep = Roo.isGecko ? "+\n" : ",\n";
12431         
12432         var undef = function(str) {
12433             Roo.debug && Roo.log("Property not found :"  + str);
12434             return '';
12435         };
12436           
12437         //Roo.log(tpl.body);
12438         
12439         
12440         
12441         var fn = function(m, lbrace, name, format, args)
12442         {
12443             //Roo.log("ARGS");
12444             //Roo.log(arguments);
12445             args = args ? args.replace(/\\'/g,"'") : args;
12446             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12447             if (typeof(format) == 'undefined') {
12448                 format =  'htmlEncode'; 
12449             }
12450             if (format == 'raw' ) {
12451                 format = false;
12452             }
12453             
12454             if(name.substr(0, 6) == 'domtpl'){
12455                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12456             }
12457             
12458             // build an array of options to determine if value is undefined..
12459             
12460             // basically get 'xxxx.yyyy' then do
12461             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12462             //    (function () { Roo.log("Property not found"); return ''; })() :
12463             //    ......
12464             
12465             var udef_ar = [];
12466             var lookfor = '';
12467             Roo.each(name.split('.'), function(st) {
12468                 lookfor += (lookfor.length ? '.': '') + st;
12469                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12470             });
12471             
12472             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12473             
12474             
12475             if(format && useF){
12476                 
12477                 args = args ? ',' + args : "";
12478                  
12479                 if(format.substr(0, 5) != "this."){
12480                     format = "fm." + format + '(';
12481                 }else{
12482                     format = 'this.call("'+ format.substr(5) + '", ';
12483                     args = ", values";
12484                 }
12485                 
12486                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12487             }
12488              
12489             if (args && args.length) {
12490                 // called with xxyx.yuu:(test,test)
12491                 // change to ()
12492                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12493             }
12494             // raw.. - :raw modifier..
12495             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12496             
12497         };
12498         var body;
12499         // branched to use + in gecko and [].join() in others
12500         if(Roo.isGecko){
12501             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12502                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12503                     "';};};";
12504         }else{
12505             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12506             body.push(tpl.body.replace(/(\r\n|\n)/g,
12507                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12508             body.push("'].join('');};};");
12509             body = body.join('');
12510         }
12511         
12512         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12513        
12514         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12515         eval(body);
12516         
12517         return this;
12518     },
12519      
12520     /**
12521      * same as applyTemplate, except it's done to one of the subTemplates
12522      * when using named templates, you can do:
12523      *
12524      * var str = pl.applySubTemplate('your-name', values);
12525      *
12526      * 
12527      * @param {Number} id of the template
12528      * @param {Object} values to apply to template
12529      * @param {Object} parent (normaly the instance of this object)
12530      */
12531     applySubTemplate : function(id, values, parent)
12532     {
12533         
12534         
12535         var t = this.tpls[id];
12536         
12537         
12538         try { 
12539             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12540                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12541                 return '';
12542             }
12543         } catch(e) {
12544             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12545             Roo.log(values);
12546           
12547             return '';
12548         }
12549         try { 
12550             
12551             if(t.execCall && t.execCall.call(this, values, parent)){
12552                 return '';
12553             }
12554         } catch(e) {
12555             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12556             Roo.log(values);
12557             return '';
12558         }
12559         
12560         try {
12561             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12562             parent = t.target ? values : parent;
12563             if(t.forCall && vs instanceof Array){
12564                 var buf = [];
12565                 for(var i = 0, len = vs.length; i < len; i++){
12566                     try {
12567                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12568                     } catch (e) {
12569                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12570                         Roo.log(e.body);
12571                         //Roo.log(t.compiled);
12572                         Roo.log(vs[i]);
12573                     }   
12574                 }
12575                 return buf.join('');
12576             }
12577         } catch (e) {
12578             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12579             Roo.log(values);
12580             return '';
12581         }
12582         try {
12583             return t.compiled.call(this, vs, parent);
12584         } catch (e) {
12585             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12586             Roo.log(e.body);
12587             //Roo.log(t.compiled);
12588             Roo.log(values);
12589             return '';
12590         }
12591     },
12592
12593    
12594
12595     applyTemplate : function(values){
12596         return this.master.compiled.call(this, values, {});
12597         //var s = this.subs;
12598     },
12599
12600     apply : function(){
12601         return this.applyTemplate.apply(this, arguments);
12602     }
12603
12604  });
12605
12606 Roo.DomTemplate.from = function(el){
12607     el = Roo.getDom(el);
12608     return new Roo.Domtemplate(el.value || el.innerHTML);
12609 };/*
12610  * Based on:
12611  * Ext JS Library 1.1.1
12612  * Copyright(c) 2006-2007, Ext JS, LLC.
12613  *
12614  * Originally Released Under LGPL - original licence link has changed is not relivant.
12615  *
12616  * Fork - LGPL
12617  * <script type="text/javascript">
12618  */
12619
12620 /**
12621  * @class Roo.util.DelayedTask
12622  * Provides a convenient method of performing setTimeout where a new
12623  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12624  * You can use this class to buffer
12625  * the keypress events for a certain number of milliseconds, and perform only if they stop
12626  * for that amount of time.
12627  * @constructor The parameters to this constructor serve as defaults and are not required.
12628  * @param {Function} fn (optional) The default function to timeout
12629  * @param {Object} scope (optional) The default scope of that timeout
12630  * @param {Array} args (optional) The default Array of arguments
12631  */
12632 Roo.util.DelayedTask = function(fn, scope, args){
12633     var id = null, d, t;
12634
12635     var call = function(){
12636         var now = new Date().getTime();
12637         if(now - t >= d){
12638             clearInterval(id);
12639             id = null;
12640             fn.apply(scope, args || []);
12641         }
12642     };
12643     /**
12644      * Cancels any pending timeout and queues a new one
12645      * @param {Number} delay The milliseconds to delay
12646      * @param {Function} newFn (optional) Overrides function passed to constructor
12647      * @param {Object} newScope (optional) Overrides scope passed to constructor
12648      * @param {Array} newArgs (optional) Overrides args passed to constructor
12649      */
12650     this.delay = function(delay, newFn, newScope, newArgs){
12651         if(id && delay != d){
12652             this.cancel();
12653         }
12654         d = delay;
12655         t = new Date().getTime();
12656         fn = newFn || fn;
12657         scope = newScope || scope;
12658         args = newArgs || args;
12659         if(!id){
12660             id = setInterval(call, d);
12661         }
12662     };
12663
12664     /**
12665      * Cancel the last queued timeout
12666      */
12667     this.cancel = function(){
12668         if(id){
12669             clearInterval(id);
12670             id = null;
12671         }
12672     };
12673 };/*
12674  * Based on:
12675  * Ext JS Library 1.1.1
12676  * Copyright(c) 2006-2007, Ext JS, LLC.
12677  *
12678  * Originally Released Under LGPL - original licence link has changed is not relivant.
12679  *
12680  * Fork - LGPL
12681  * <script type="text/javascript">
12682  */
12683  
12684  
12685 Roo.util.TaskRunner = function(interval){
12686     interval = interval || 10;
12687     var tasks = [], removeQueue = [];
12688     var id = 0;
12689     var running = false;
12690
12691     var stopThread = function(){
12692         running = false;
12693         clearInterval(id);
12694         id = 0;
12695     };
12696
12697     var startThread = function(){
12698         if(!running){
12699             running = true;
12700             id = setInterval(runTasks, interval);
12701         }
12702     };
12703
12704     var removeTask = function(task){
12705         removeQueue.push(task);
12706         if(task.onStop){
12707             task.onStop();
12708         }
12709     };
12710
12711     var runTasks = function(){
12712         if(removeQueue.length > 0){
12713             for(var i = 0, len = removeQueue.length; i < len; i++){
12714                 tasks.remove(removeQueue[i]);
12715             }
12716             removeQueue = [];
12717             if(tasks.length < 1){
12718                 stopThread();
12719                 return;
12720             }
12721         }
12722         var now = new Date().getTime();
12723         for(var i = 0, len = tasks.length; i < len; ++i){
12724             var t = tasks[i];
12725             var itime = now - t.taskRunTime;
12726             if(t.interval <= itime){
12727                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12728                 t.taskRunTime = now;
12729                 if(rt === false || t.taskRunCount === t.repeat){
12730                     removeTask(t);
12731                     return;
12732                 }
12733             }
12734             if(t.duration && t.duration <= (now - t.taskStartTime)){
12735                 removeTask(t);
12736             }
12737         }
12738     };
12739
12740     /**
12741      * Queues a new task.
12742      * @param {Object} task
12743      */
12744     this.start = function(task){
12745         tasks.push(task);
12746         task.taskStartTime = new Date().getTime();
12747         task.taskRunTime = 0;
12748         task.taskRunCount = 0;
12749         startThread();
12750         return task;
12751     };
12752
12753     this.stop = function(task){
12754         removeTask(task);
12755         return task;
12756     };
12757
12758     this.stopAll = function(){
12759         stopThread();
12760         for(var i = 0, len = tasks.length; i < len; i++){
12761             if(tasks[i].onStop){
12762                 tasks[i].onStop();
12763             }
12764         }
12765         tasks = [];
12766         removeQueue = [];
12767     };
12768 };
12769
12770 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12771  * Based on:
12772  * Ext JS Library 1.1.1
12773  * Copyright(c) 2006-2007, Ext JS, LLC.
12774  *
12775  * Originally Released Under LGPL - original licence link has changed is not relivant.
12776  *
12777  * Fork - LGPL
12778  * <script type="text/javascript">
12779  */
12780
12781  
12782 /**
12783  * @class Roo.util.MixedCollection
12784  * @extends Roo.util.Observable
12785  * A Collection class that maintains both numeric indexes and keys and exposes events.
12786  * @constructor
12787  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12788  * collection (defaults to false)
12789  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12790  * and return the key value for that item.  This is used when available to look up the key on items that
12791  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12792  * equivalent to providing an implementation for the {@link #getKey} method.
12793  */
12794 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12795     this.items = [];
12796     this.map = {};
12797     this.keys = [];
12798     this.length = 0;
12799     this.addEvents({
12800         /**
12801          * @event clear
12802          * Fires when the collection is cleared.
12803          */
12804         "clear" : true,
12805         /**
12806          * @event add
12807          * Fires when an item is added to the collection.
12808          * @param {Number} index The index at which the item was added.
12809          * @param {Object} o The item added.
12810          * @param {String} key The key associated with the added item.
12811          */
12812         "add" : true,
12813         /**
12814          * @event replace
12815          * Fires when an item is replaced in the collection.
12816          * @param {String} key he key associated with the new added.
12817          * @param {Object} old The item being replaced.
12818          * @param {Object} new The new item.
12819          */
12820         "replace" : true,
12821         /**
12822          * @event remove
12823          * Fires when an item is removed from the collection.
12824          * @param {Object} o The item being removed.
12825          * @param {String} key (optional) The key associated with the removed item.
12826          */
12827         "remove" : true,
12828         "sort" : true
12829     });
12830     this.allowFunctions = allowFunctions === true;
12831     if(keyFn){
12832         this.getKey = keyFn;
12833     }
12834     Roo.util.MixedCollection.superclass.constructor.call(this);
12835 };
12836
12837 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12838     allowFunctions : false,
12839     
12840 /**
12841  * Adds an item to the collection.
12842  * @param {String} key The key to associate with the item
12843  * @param {Object} o The item to add.
12844  * @return {Object} The item added.
12845  */
12846     add : function(key, o){
12847         if(arguments.length == 1){
12848             o = arguments[0];
12849             key = this.getKey(o);
12850         }
12851         if(typeof key == "undefined" || key === null){
12852             this.length++;
12853             this.items.push(o);
12854             this.keys.push(null);
12855         }else{
12856             var old = this.map[key];
12857             if(old){
12858                 return this.replace(key, o);
12859             }
12860             this.length++;
12861             this.items.push(o);
12862             this.map[key] = o;
12863             this.keys.push(key);
12864         }
12865         this.fireEvent("add", this.length-1, o, key);
12866         return o;
12867     },
12868        
12869 /**
12870   * MixedCollection has a generic way to fetch keys if you implement getKey.
12871 <pre><code>
12872 // normal way
12873 var mc = new Roo.util.MixedCollection();
12874 mc.add(someEl.dom.id, someEl);
12875 mc.add(otherEl.dom.id, otherEl);
12876 //and so on
12877
12878 // using getKey
12879 var mc = new Roo.util.MixedCollection();
12880 mc.getKey = function(el){
12881    return el.dom.id;
12882 };
12883 mc.add(someEl);
12884 mc.add(otherEl);
12885
12886 // or via the constructor
12887 var mc = new Roo.util.MixedCollection(false, function(el){
12888    return el.dom.id;
12889 });
12890 mc.add(someEl);
12891 mc.add(otherEl);
12892 </code></pre>
12893  * @param o {Object} The item for which to find the key.
12894  * @return {Object} The key for the passed item.
12895  */
12896     getKey : function(o){
12897          return o.id; 
12898     },
12899    
12900 /**
12901  * Replaces an item in the collection.
12902  * @param {String} key The key associated with the item to replace, or the item to replace.
12903  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12904  * @return {Object}  The new item.
12905  */
12906     replace : function(key, o){
12907         if(arguments.length == 1){
12908             o = arguments[0];
12909             key = this.getKey(o);
12910         }
12911         var old = this.item(key);
12912         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12913              return this.add(key, o);
12914         }
12915         var index = this.indexOfKey(key);
12916         this.items[index] = o;
12917         this.map[key] = o;
12918         this.fireEvent("replace", key, old, o);
12919         return o;
12920     },
12921    
12922 /**
12923  * Adds all elements of an Array or an Object to the collection.
12924  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12925  * an Array of values, each of which are added to the collection.
12926  */
12927     addAll : function(objs){
12928         if(arguments.length > 1 || objs instanceof Array){
12929             var args = arguments.length > 1 ? arguments : objs;
12930             for(var i = 0, len = args.length; i < len; i++){
12931                 this.add(args[i]);
12932             }
12933         }else{
12934             for(var key in objs){
12935                 if(this.allowFunctions || typeof objs[key] != "function"){
12936                     this.add(key, objs[key]);
12937                 }
12938             }
12939         }
12940     },
12941    
12942 /**
12943  * Executes the specified function once for every item in the collection, passing each
12944  * item as the first and only parameter. returning false from the function will stop the iteration.
12945  * @param {Function} fn The function to execute for each item.
12946  * @param {Object} scope (optional) The scope in which to execute the function.
12947  */
12948     each : function(fn, scope){
12949         var items = [].concat(this.items); // each safe for removal
12950         for(var i = 0, len = items.length; i < len; i++){
12951             if(fn.call(scope || items[i], items[i], i, len) === false){
12952                 break;
12953             }
12954         }
12955     },
12956    
12957 /**
12958  * Executes the specified function once for every key in the collection, passing each
12959  * key, and its associated item as the first two parameters.
12960  * @param {Function} fn The function to execute for each item.
12961  * @param {Object} scope (optional) The scope in which to execute the function.
12962  */
12963     eachKey : function(fn, scope){
12964         for(var i = 0, len = this.keys.length; i < len; i++){
12965             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12966         }
12967     },
12968    
12969 /**
12970  * Returns the first item in the collection which elicits a true return value from the
12971  * passed selection function.
12972  * @param {Function} fn The selection function to execute for each item.
12973  * @param {Object} scope (optional) The scope in which to execute the function.
12974  * @return {Object} The first item in the collection which returned true from the selection function.
12975  */
12976     find : function(fn, scope){
12977         for(var i = 0, len = this.items.length; i < len; i++){
12978             if(fn.call(scope || window, this.items[i], this.keys[i])){
12979                 return this.items[i];
12980             }
12981         }
12982         return null;
12983     },
12984    
12985 /**
12986  * Inserts an item at the specified index in the collection.
12987  * @param {Number} index The index to insert the item at.
12988  * @param {String} key The key to associate with the new item, or the item itself.
12989  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12990  * @return {Object} The item inserted.
12991  */
12992     insert : function(index, key, o){
12993         if(arguments.length == 2){
12994             o = arguments[1];
12995             key = this.getKey(o);
12996         }
12997         if(index >= this.length){
12998             return this.add(key, o);
12999         }
13000         this.length++;
13001         this.items.splice(index, 0, o);
13002         if(typeof key != "undefined" && key != null){
13003             this.map[key] = o;
13004         }
13005         this.keys.splice(index, 0, key);
13006         this.fireEvent("add", index, o, key);
13007         return o;
13008     },
13009    
13010 /**
13011  * Removed an item from the collection.
13012  * @param {Object} o The item to remove.
13013  * @return {Object} The item removed.
13014  */
13015     remove : function(o){
13016         return this.removeAt(this.indexOf(o));
13017     },
13018    
13019 /**
13020  * Remove an item from a specified index in the collection.
13021  * @param {Number} index The index within the collection of the item to remove.
13022  */
13023     removeAt : function(index){
13024         if(index < this.length && index >= 0){
13025             this.length--;
13026             var o = this.items[index];
13027             this.items.splice(index, 1);
13028             var key = this.keys[index];
13029             if(typeof key != "undefined"){
13030                 delete this.map[key];
13031             }
13032             this.keys.splice(index, 1);
13033             this.fireEvent("remove", o, key);
13034         }
13035     },
13036    
13037 /**
13038  * Removed an item associated with the passed key fom the collection.
13039  * @param {String} key The key of the item to remove.
13040  */
13041     removeKey : function(key){
13042         return this.removeAt(this.indexOfKey(key));
13043     },
13044    
13045 /**
13046  * Returns the number of items in the collection.
13047  * @return {Number} the number of items in the collection.
13048  */
13049     getCount : function(){
13050         return this.length; 
13051     },
13052    
13053 /**
13054  * Returns index within the collection of the passed Object.
13055  * @param {Object} o The item to find the index of.
13056  * @return {Number} index of the item.
13057  */
13058     indexOf : function(o){
13059         if(!this.items.indexOf){
13060             for(var i = 0, len = this.items.length; i < len; i++){
13061                 if(this.items[i] == o) return i;
13062             }
13063             return -1;
13064         }else{
13065             return this.items.indexOf(o);
13066         }
13067     },
13068    
13069 /**
13070  * Returns index within the collection of the passed key.
13071  * @param {String} key The key to find the index of.
13072  * @return {Number} index of the key.
13073  */
13074     indexOfKey : function(key){
13075         if(!this.keys.indexOf){
13076             for(var i = 0, len = this.keys.length; i < len; i++){
13077                 if(this.keys[i] == key) return i;
13078             }
13079             return -1;
13080         }else{
13081             return this.keys.indexOf(key);
13082         }
13083     },
13084    
13085 /**
13086  * Returns the item associated with the passed key OR index. Key has priority over index.
13087  * @param {String/Number} key The key or index of the item.
13088  * @return {Object} The item associated with the passed key.
13089  */
13090     item : function(key){
13091         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13092         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13093     },
13094     
13095 /**
13096  * Returns the item at the specified index.
13097  * @param {Number} index The index of the item.
13098  * @return {Object}
13099  */
13100     itemAt : function(index){
13101         return this.items[index];
13102     },
13103     
13104 /**
13105  * Returns the item associated with the passed key.
13106  * @param {String/Number} key The key of the item.
13107  * @return {Object} The item associated with the passed key.
13108  */
13109     key : function(key){
13110         return this.map[key];
13111     },
13112    
13113 /**
13114  * Returns true if the collection contains the passed Object as an item.
13115  * @param {Object} o  The Object to look for in the collection.
13116  * @return {Boolean} True if the collection contains the Object as an item.
13117  */
13118     contains : function(o){
13119         return this.indexOf(o) != -1;
13120     },
13121    
13122 /**
13123  * Returns true if the collection contains the passed Object as a key.
13124  * @param {String} key The key to look for in the collection.
13125  * @return {Boolean} True if the collection contains the Object as a key.
13126  */
13127     containsKey : function(key){
13128         return typeof this.map[key] != "undefined";
13129     },
13130    
13131 /**
13132  * Removes all items from the collection.
13133  */
13134     clear : function(){
13135         this.length = 0;
13136         this.items = [];
13137         this.keys = [];
13138         this.map = {};
13139         this.fireEvent("clear");
13140     },
13141    
13142 /**
13143  * Returns the first item in the collection.
13144  * @return {Object} the first item in the collection..
13145  */
13146     first : function(){
13147         return this.items[0]; 
13148     },
13149    
13150 /**
13151  * Returns the last item in the collection.
13152  * @return {Object} the last item in the collection..
13153  */
13154     last : function(){
13155         return this.items[this.length-1];   
13156     },
13157     
13158     _sort : function(property, dir, fn){
13159         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13160         fn = fn || function(a, b){
13161             return a-b;
13162         };
13163         var c = [], k = this.keys, items = this.items;
13164         for(var i = 0, len = items.length; i < len; i++){
13165             c[c.length] = {key: k[i], value: items[i], index: i};
13166         }
13167         c.sort(function(a, b){
13168             var v = fn(a[property], b[property]) * dsc;
13169             if(v == 0){
13170                 v = (a.index < b.index ? -1 : 1);
13171             }
13172             return v;
13173         });
13174         for(var i = 0, len = c.length; i < len; i++){
13175             items[i] = c[i].value;
13176             k[i] = c[i].key;
13177         }
13178         this.fireEvent("sort", this);
13179     },
13180     
13181     /**
13182      * Sorts this collection with the passed comparison function
13183      * @param {String} direction (optional) "ASC" or "DESC"
13184      * @param {Function} fn (optional) comparison function
13185      */
13186     sort : function(dir, fn){
13187         this._sort("value", dir, fn);
13188     },
13189     
13190     /**
13191      * Sorts this collection by keys
13192      * @param {String} direction (optional) "ASC" or "DESC"
13193      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13194      */
13195     keySort : function(dir, fn){
13196         this._sort("key", dir, fn || function(a, b){
13197             return String(a).toUpperCase()-String(b).toUpperCase();
13198         });
13199     },
13200     
13201     /**
13202      * Returns a range of items in this collection
13203      * @param {Number} startIndex (optional) defaults to 0
13204      * @param {Number} endIndex (optional) default to the last item
13205      * @return {Array} An array of items
13206      */
13207     getRange : function(start, end){
13208         var items = this.items;
13209         if(items.length < 1){
13210             return [];
13211         }
13212         start = start || 0;
13213         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13214         var r = [];
13215         if(start <= end){
13216             for(var i = start; i <= end; i++) {
13217                     r[r.length] = items[i];
13218             }
13219         }else{
13220             for(var i = start; i >= end; i--) {
13221                     r[r.length] = items[i];
13222             }
13223         }
13224         return r;
13225     },
13226         
13227     /**
13228      * Filter the <i>objects</i> in this collection by a specific property. 
13229      * Returns a new collection that has been filtered.
13230      * @param {String} property A property on your objects
13231      * @param {String/RegExp} value Either string that the property values 
13232      * should start with or a RegExp to test against the property
13233      * @return {MixedCollection} The new filtered collection
13234      */
13235     filter : function(property, value){
13236         if(!value.exec){ // not a regex
13237             value = String(value);
13238             if(value.length == 0){
13239                 return this.clone();
13240             }
13241             value = new RegExp("^" + Roo.escapeRe(value), "i");
13242         }
13243         return this.filterBy(function(o){
13244             return o && value.test(o[property]);
13245         });
13246         },
13247     
13248     /**
13249      * Filter by a function. * Returns a new collection that has been filtered.
13250      * The passed function will be called with each 
13251      * object in the collection. If the function returns true, the value is included 
13252      * otherwise it is filtered.
13253      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13254      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13255      * @return {MixedCollection} The new filtered collection
13256      */
13257     filterBy : function(fn, scope){
13258         var r = new Roo.util.MixedCollection();
13259         r.getKey = this.getKey;
13260         var k = this.keys, it = this.items;
13261         for(var i = 0, len = it.length; i < len; i++){
13262             if(fn.call(scope||this, it[i], k[i])){
13263                                 r.add(k[i], it[i]);
13264                         }
13265         }
13266         return r;
13267     },
13268     
13269     /**
13270      * Creates a duplicate of this collection
13271      * @return {MixedCollection}
13272      */
13273     clone : function(){
13274         var r = new Roo.util.MixedCollection();
13275         var k = this.keys, it = this.items;
13276         for(var i = 0, len = it.length; i < len; i++){
13277             r.add(k[i], it[i]);
13278         }
13279         r.getKey = this.getKey;
13280         return r;
13281     }
13282 });
13283 /**
13284  * Returns the item associated with the passed key or index.
13285  * @method
13286  * @param {String/Number} key The key or index of the item.
13287  * @return {Object} The item associated with the passed key.
13288  */
13289 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13290  * Based on:
13291  * Ext JS Library 1.1.1
13292  * Copyright(c) 2006-2007, Ext JS, LLC.
13293  *
13294  * Originally Released Under LGPL - original licence link has changed is not relivant.
13295  *
13296  * Fork - LGPL
13297  * <script type="text/javascript">
13298  */
13299 /**
13300  * @class Roo.util.JSON
13301  * Modified version of Douglas Crockford"s json.js that doesn"t
13302  * mess with the Object prototype 
13303  * http://www.json.org/js.html
13304  * @singleton
13305  */
13306 Roo.util.JSON = new (function(){
13307     var useHasOwn = {}.hasOwnProperty ? true : false;
13308     
13309     // crashes Safari in some instances
13310     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13311     
13312     var pad = function(n) {
13313         return n < 10 ? "0" + n : n;
13314     };
13315     
13316     var m = {
13317         "\b": '\\b',
13318         "\t": '\\t',
13319         "\n": '\\n',
13320         "\f": '\\f',
13321         "\r": '\\r',
13322         '"' : '\\"',
13323         "\\": '\\\\'
13324     };
13325
13326     var encodeString = function(s){
13327         if (/["\\\x00-\x1f]/.test(s)) {
13328             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13329                 var c = m[b];
13330                 if(c){
13331                     return c;
13332                 }
13333                 c = b.charCodeAt();
13334                 return "\\u00" +
13335                     Math.floor(c / 16).toString(16) +
13336                     (c % 16).toString(16);
13337             }) + '"';
13338         }
13339         return '"' + s + '"';
13340     };
13341     
13342     var encodeArray = function(o){
13343         var a = ["["], b, i, l = o.length, v;
13344             for (i = 0; i < l; i += 1) {
13345                 v = o[i];
13346                 switch (typeof v) {
13347                     case "undefined":
13348                     case "function":
13349                     case "unknown":
13350                         break;
13351                     default:
13352                         if (b) {
13353                             a.push(',');
13354                         }
13355                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13356                         b = true;
13357                 }
13358             }
13359             a.push("]");
13360             return a.join("");
13361     };
13362     
13363     var encodeDate = function(o){
13364         return '"' + o.getFullYear() + "-" +
13365                 pad(o.getMonth() + 1) + "-" +
13366                 pad(o.getDate()) + "T" +
13367                 pad(o.getHours()) + ":" +
13368                 pad(o.getMinutes()) + ":" +
13369                 pad(o.getSeconds()) + '"';
13370     };
13371     
13372     /**
13373      * Encodes an Object, Array or other value
13374      * @param {Mixed} o The variable to encode
13375      * @return {String} The JSON string
13376      */
13377     this.encode = function(o)
13378     {
13379         // should this be extended to fully wrap stringify..
13380         
13381         if(typeof o == "undefined" || o === null){
13382             return "null";
13383         }else if(o instanceof Array){
13384             return encodeArray(o);
13385         }else if(o instanceof Date){
13386             return encodeDate(o);
13387         }else if(typeof o == "string"){
13388             return encodeString(o);
13389         }else if(typeof o == "number"){
13390             return isFinite(o) ? String(o) : "null";
13391         }else if(typeof o == "boolean"){
13392             return String(o);
13393         }else {
13394             var a = ["{"], b, i, v;
13395             for (i in o) {
13396                 if(!useHasOwn || o.hasOwnProperty(i)) {
13397                     v = o[i];
13398                     switch (typeof v) {
13399                     case "undefined":
13400                     case "function":
13401                     case "unknown":
13402                         break;
13403                     default:
13404                         if(b){
13405                             a.push(',');
13406                         }
13407                         a.push(this.encode(i), ":",
13408                                 v === null ? "null" : this.encode(v));
13409                         b = true;
13410                     }
13411                 }
13412             }
13413             a.push("}");
13414             return a.join("");
13415         }
13416     };
13417     
13418     /**
13419      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13420      * @param {String} json The JSON string
13421      * @return {Object} The resulting object
13422      */
13423     this.decode = function(json){
13424         
13425         return  /** eval:var:json */ eval("(" + json + ')');
13426     };
13427 })();
13428 /** 
13429  * Shorthand for {@link Roo.util.JSON#encode}
13430  * @member Roo encode 
13431  * @method */
13432 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13433 /** 
13434  * Shorthand for {@link Roo.util.JSON#decode}
13435  * @member Roo decode 
13436  * @method */
13437 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13438 /*
13439  * Based on:
13440  * Ext JS Library 1.1.1
13441  * Copyright(c) 2006-2007, Ext JS, LLC.
13442  *
13443  * Originally Released Under LGPL - original licence link has changed is not relivant.
13444  *
13445  * Fork - LGPL
13446  * <script type="text/javascript">
13447  */
13448  
13449 /**
13450  * @class Roo.util.Format
13451  * Reusable data formatting functions
13452  * @singleton
13453  */
13454 Roo.util.Format = function(){
13455     var trimRe = /^\s+|\s+$/g;
13456     return {
13457         /**
13458          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13459          * @param {String} value The string to truncate
13460          * @param {Number} length The maximum length to allow before truncating
13461          * @return {String} The converted text
13462          */
13463         ellipsis : function(value, len){
13464             if(value && value.length > len){
13465                 return value.substr(0, len-3)+"...";
13466             }
13467             return value;
13468         },
13469
13470         /**
13471          * Checks a reference and converts it to empty string if it is undefined
13472          * @param {Mixed} value Reference to check
13473          * @return {Mixed} Empty string if converted, otherwise the original value
13474          */
13475         undef : function(value){
13476             return typeof value != "undefined" ? value : "";
13477         },
13478
13479         /**
13480          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13481          * @param {String} value The string to encode
13482          * @return {String} The encoded text
13483          */
13484         htmlEncode : function(value){
13485             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13486         },
13487
13488         /**
13489          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13490          * @param {String} value The string to decode
13491          * @return {String} The decoded text
13492          */
13493         htmlDecode : function(value){
13494             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13495         },
13496
13497         /**
13498          * Trims any whitespace from either side of a string
13499          * @param {String} value The text to trim
13500          * @return {String} The trimmed text
13501          */
13502         trim : function(value){
13503             return String(value).replace(trimRe, "");
13504         },
13505
13506         /**
13507          * Returns a substring from within an original string
13508          * @param {String} value The original text
13509          * @param {Number} start The start index of the substring
13510          * @param {Number} length The length of the substring
13511          * @return {String} The substring
13512          */
13513         substr : function(value, start, length){
13514             return String(value).substr(start, length);
13515         },
13516
13517         /**
13518          * Converts a string to all lower case letters
13519          * @param {String} value The text to convert
13520          * @return {String} The converted text
13521          */
13522         lowercase : function(value){
13523             return String(value).toLowerCase();
13524         },
13525
13526         /**
13527          * Converts a string to all upper case letters
13528          * @param {String} value The text to convert
13529          * @return {String} The converted text
13530          */
13531         uppercase : function(value){
13532             return String(value).toUpperCase();
13533         },
13534
13535         /**
13536          * Converts the first character only of a string to upper case
13537          * @param {String} value The text to convert
13538          * @return {String} The converted text
13539          */
13540         capitalize : function(value){
13541             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13542         },
13543
13544         // private
13545         call : function(value, fn){
13546             if(arguments.length > 2){
13547                 var args = Array.prototype.slice.call(arguments, 2);
13548                 args.unshift(value);
13549                  
13550                 return /** eval:var:value */  eval(fn).apply(window, args);
13551             }else{
13552                 /** eval:var:value */
13553                 return /** eval:var:value */ eval(fn).call(window, value);
13554             }
13555         },
13556
13557        
13558         /**
13559          * safer version of Math.toFixed..??/
13560          * @param {Number/String} value The numeric value to format
13561          * @param {Number/String} value Decimal places 
13562          * @return {String} The formatted currency string
13563          */
13564         toFixed : function(v, n)
13565         {
13566             // why not use to fixed - precision is buggered???
13567             if (!n) {
13568                 return Math.round(v-0);
13569             }
13570             var fact = Math.pow(10,n+1);
13571             v = (Math.round((v-0)*fact))/fact;
13572             var z = (''+fact).substring(2);
13573             if (v == Math.floor(v)) {
13574                 return Math.floor(v) + '.' + z;
13575             }
13576             
13577             // now just padd decimals..
13578             var ps = String(v).split('.');
13579             var fd = (ps[1] + z);
13580             var r = fd.substring(0,n); 
13581             var rm = fd.substring(n); 
13582             if (rm < 5) {
13583                 return ps[0] + '.' + r;
13584             }
13585             r*=1; // turn it into a number;
13586             r++;
13587             if (String(r).length != n) {
13588                 ps[0]*=1;
13589                 ps[0]++;
13590                 r = String(r).substring(1); // chop the end off.
13591             }
13592             
13593             return ps[0] + '.' + r;
13594              
13595         },
13596         
13597         /**
13598          * Format a number as US currency
13599          * @param {Number/String} value The numeric value to format
13600          * @return {String} The formatted currency string
13601          */
13602         usMoney : function(v){
13603             return '$' + Roo.util.Format.number(v);
13604         },
13605         
13606         /**
13607          * Format a number
13608          * eventually this should probably emulate php's number_format
13609          * @param {Number/String} value The numeric value to format
13610          * @param {Number} decimals number of decimal places
13611          * @return {String} The formatted currency string
13612          */
13613         number : function(v,decimals)
13614         {
13615             // multiply and round.
13616             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13617             var mul = Math.pow(10, decimals);
13618             var zero = String(mul).substring(1);
13619             v = (Math.round((v-0)*mul))/mul;
13620             
13621             // if it's '0' number.. then
13622             
13623             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13624             v = String(v);
13625             var ps = v.split('.');
13626             var whole = ps[0];
13627             
13628             
13629             var r = /(\d+)(\d{3})/;
13630             // add comma's
13631             while (r.test(whole)) {
13632                 whole = whole.replace(r, '$1' + ',' + '$2');
13633             }
13634             
13635             
13636             var sub = ps[1] ?
13637                     // has decimals..
13638                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13639                     // does not have decimals
13640                     (decimals ? ('.' + zero) : '');
13641             
13642             
13643             return whole + sub ;
13644         },
13645         
13646         /**
13647          * Parse a value into a formatted date using the specified format pattern.
13648          * @param {Mixed} value The value to format
13649          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13650          * @return {String} The formatted date string
13651          */
13652         date : function(v, format){
13653             if(!v){
13654                 return "";
13655             }
13656             if(!(v instanceof Date)){
13657                 v = new Date(Date.parse(v));
13658             }
13659             return v.dateFormat(format || Roo.util.Format.defaults.date);
13660         },
13661
13662         /**
13663          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13664          * @param {String} format Any valid date format string
13665          * @return {Function} The date formatting function
13666          */
13667         dateRenderer : function(format){
13668             return function(v){
13669                 return Roo.util.Format.date(v, format);  
13670             };
13671         },
13672
13673         // private
13674         stripTagsRE : /<\/?[^>]+>/gi,
13675         
13676         /**
13677          * Strips all HTML tags
13678          * @param {Mixed} value The text from which to strip tags
13679          * @return {String} The stripped text
13680          */
13681         stripTags : function(v){
13682             return !v ? v : String(v).replace(this.stripTagsRE, "");
13683         }
13684     };
13685 }();
13686 Roo.util.Format.defaults = {
13687     date : 'd/M/Y'
13688 };/*
13689  * Based on:
13690  * Ext JS Library 1.1.1
13691  * Copyright(c) 2006-2007, Ext JS, LLC.
13692  *
13693  * Originally Released Under LGPL - original licence link has changed is not relivant.
13694  *
13695  * Fork - LGPL
13696  * <script type="text/javascript">
13697  */
13698
13699
13700  
13701
13702 /**
13703  * @class Roo.MasterTemplate
13704  * @extends Roo.Template
13705  * Provides a template that can have child templates. The syntax is:
13706 <pre><code>
13707 var t = new Roo.MasterTemplate(
13708         '&lt;select name="{name}"&gt;',
13709                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13710         '&lt;/select&gt;'
13711 );
13712 t.add('options', {value: 'foo', text: 'bar'});
13713 // or you can add multiple child elements in one shot
13714 t.addAll('options', [
13715     {value: 'foo', text: 'bar'},
13716     {value: 'foo2', text: 'bar2'},
13717     {value: 'foo3', text: 'bar3'}
13718 ]);
13719 // then append, applying the master template values
13720 t.append('my-form', {name: 'my-select'});
13721 </code></pre>
13722 * A name attribute for the child template is not required if you have only one child
13723 * template or you want to refer to them by index.
13724  */
13725 Roo.MasterTemplate = function(){
13726     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13727     this.originalHtml = this.html;
13728     var st = {};
13729     var m, re = this.subTemplateRe;
13730     re.lastIndex = 0;
13731     var subIndex = 0;
13732     while(m = re.exec(this.html)){
13733         var name = m[1], content = m[2];
13734         st[subIndex] = {
13735             name: name,
13736             index: subIndex,
13737             buffer: [],
13738             tpl : new Roo.Template(content)
13739         };
13740         if(name){
13741             st[name] = st[subIndex];
13742         }
13743         st[subIndex].tpl.compile();
13744         st[subIndex].tpl.call = this.call.createDelegate(this);
13745         subIndex++;
13746     }
13747     this.subCount = subIndex;
13748     this.subs = st;
13749 };
13750 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13751     /**
13752     * The regular expression used to match sub templates
13753     * @type RegExp
13754     * @property
13755     */
13756     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13757
13758     /**
13759      * Applies the passed values to a child template.
13760      * @param {String/Number} name (optional) The name or index of the child template
13761      * @param {Array/Object} values The values to be applied to the template
13762      * @return {MasterTemplate} this
13763      */
13764      add : function(name, values){
13765         if(arguments.length == 1){
13766             values = arguments[0];
13767             name = 0;
13768         }
13769         var s = this.subs[name];
13770         s.buffer[s.buffer.length] = s.tpl.apply(values);
13771         return this;
13772     },
13773
13774     /**
13775      * Applies all the passed values to a child template.
13776      * @param {String/Number} name (optional) The name or index of the child template
13777      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13778      * @param {Boolean} reset (optional) True to reset the template first
13779      * @return {MasterTemplate} this
13780      */
13781     fill : function(name, values, reset){
13782         var a = arguments;
13783         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13784             values = a[0];
13785             name = 0;
13786             reset = a[1];
13787         }
13788         if(reset){
13789             this.reset();
13790         }
13791         for(var i = 0, len = values.length; i < len; i++){
13792             this.add(name, values[i]);
13793         }
13794         return this;
13795     },
13796
13797     /**
13798      * Resets the template for reuse
13799      * @return {MasterTemplate} this
13800      */
13801      reset : function(){
13802         var s = this.subs;
13803         for(var i = 0; i < this.subCount; i++){
13804             s[i].buffer = [];
13805         }
13806         return this;
13807     },
13808
13809     applyTemplate : function(values){
13810         var s = this.subs;
13811         var replaceIndex = -1;
13812         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13813             return s[++replaceIndex].buffer.join("");
13814         });
13815         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13816     },
13817
13818     apply : function(){
13819         return this.applyTemplate.apply(this, arguments);
13820     },
13821
13822     compile : function(){return this;}
13823 });
13824
13825 /**
13826  * Alias for fill().
13827  * @method
13828  */
13829 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13830  /**
13831  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13832  * var tpl = Roo.MasterTemplate.from('element-id');
13833  * @param {String/HTMLElement} el
13834  * @param {Object} config
13835  * @static
13836  */
13837 Roo.MasterTemplate.from = function(el, config){
13838     el = Roo.getDom(el);
13839     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13840 };/*
13841  * Based on:
13842  * Ext JS Library 1.1.1
13843  * Copyright(c) 2006-2007, Ext JS, LLC.
13844  *
13845  * Originally Released Under LGPL - original licence link has changed is not relivant.
13846  *
13847  * Fork - LGPL
13848  * <script type="text/javascript">
13849  */
13850
13851  
13852 /**
13853  * @class Roo.util.CSS
13854  * Utility class for manipulating CSS rules
13855  * @singleton
13856  */
13857 Roo.util.CSS = function(){
13858         var rules = null;
13859         var doc = document;
13860
13861     var camelRe = /(-[a-z])/gi;
13862     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13863
13864    return {
13865    /**
13866     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13867     * tag and appended to the HEAD of the document.
13868     * @param {String|Object} cssText The text containing the css rules
13869     * @param {String} id An id to add to the stylesheet for later removal
13870     * @return {StyleSheet}
13871     */
13872     createStyleSheet : function(cssText, id){
13873         var ss;
13874         var head = doc.getElementsByTagName("head")[0];
13875         var nrules = doc.createElement("style");
13876         nrules.setAttribute("type", "text/css");
13877         if(id){
13878             nrules.setAttribute("id", id);
13879         }
13880         if (typeof(cssText) != 'string') {
13881             // support object maps..
13882             // not sure if this a good idea.. 
13883             // perhaps it should be merged with the general css handling
13884             // and handle js style props.
13885             var cssTextNew = [];
13886             for(var n in cssText) {
13887                 var citems = [];
13888                 for(var k in cssText[n]) {
13889                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13890                 }
13891                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13892                 
13893             }
13894             cssText = cssTextNew.join("\n");
13895             
13896         }
13897        
13898        
13899        if(Roo.isIE){
13900            head.appendChild(nrules);
13901            ss = nrules.styleSheet;
13902            ss.cssText = cssText;
13903        }else{
13904            try{
13905                 nrules.appendChild(doc.createTextNode(cssText));
13906            }catch(e){
13907                nrules.cssText = cssText; 
13908            }
13909            head.appendChild(nrules);
13910            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13911        }
13912        this.cacheStyleSheet(ss);
13913        return ss;
13914    },
13915
13916    /**
13917     * Removes a style or link tag by id
13918     * @param {String} id The id of the tag
13919     */
13920    removeStyleSheet : function(id){
13921        var existing = doc.getElementById(id);
13922        if(existing){
13923            existing.parentNode.removeChild(existing);
13924        }
13925    },
13926
13927    /**
13928     * Dynamically swaps an existing stylesheet reference for a new one
13929     * @param {String} id The id of an existing link tag to remove
13930     * @param {String} url The href of the new stylesheet to include
13931     */
13932    swapStyleSheet : function(id, url){
13933        this.removeStyleSheet(id);
13934        var ss = doc.createElement("link");
13935        ss.setAttribute("rel", "stylesheet");
13936        ss.setAttribute("type", "text/css");
13937        ss.setAttribute("id", id);
13938        ss.setAttribute("href", url);
13939        doc.getElementsByTagName("head")[0].appendChild(ss);
13940    },
13941    
13942    /**
13943     * Refresh the rule cache if you have dynamically added stylesheets
13944     * @return {Object} An object (hash) of rules indexed by selector
13945     */
13946    refreshCache : function(){
13947        return this.getRules(true);
13948    },
13949
13950    // private
13951    cacheStyleSheet : function(stylesheet){
13952        if(!rules){
13953            rules = {};
13954        }
13955        try{// try catch for cross domain access issue
13956            var ssRules = stylesheet.cssRules || stylesheet.rules;
13957            for(var j = ssRules.length-1; j >= 0; --j){
13958                rules[ssRules[j].selectorText] = ssRules[j];
13959            }
13960        }catch(e){}
13961    },
13962    
13963    /**
13964     * Gets all css rules for the document
13965     * @param {Boolean} refreshCache true to refresh the internal cache
13966     * @return {Object} An object (hash) of rules indexed by selector
13967     */
13968    getRules : function(refreshCache){
13969                 if(rules == null || refreshCache){
13970                         rules = {};
13971                         var ds = doc.styleSheets;
13972                         for(var i =0, len = ds.length; i < len; i++){
13973                             try{
13974                         this.cacheStyleSheet(ds[i]);
13975                     }catch(e){} 
13976                 }
13977                 }
13978                 return rules;
13979         },
13980         
13981         /**
13982     * Gets an an individual CSS rule by selector(s)
13983     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13984     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13985     * @return {CSSRule} The CSS rule or null if one is not found
13986     */
13987    getRule : function(selector, refreshCache){
13988                 var rs = this.getRules(refreshCache);
13989                 if(!(selector instanceof Array)){
13990                     return rs[selector];
13991                 }
13992                 for(var i = 0; i < selector.length; i++){
13993                         if(rs[selector[i]]){
13994                                 return rs[selector[i]];
13995                         }
13996                 }
13997                 return null;
13998         },
13999         
14000         
14001         /**
14002     * Updates a rule property
14003     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14004     * @param {String} property The css property
14005     * @param {String} value The new value for the property
14006     * @return {Boolean} true If a rule was found and updated
14007     */
14008    updateRule : function(selector, property, value){
14009                 if(!(selector instanceof Array)){
14010                         var rule = this.getRule(selector);
14011                         if(rule){
14012                                 rule.style[property.replace(camelRe, camelFn)] = value;
14013                                 return true;
14014                         }
14015                 }else{
14016                         for(var i = 0; i < selector.length; i++){
14017                                 if(this.updateRule(selector[i], property, value)){
14018                                         return true;
14019                                 }
14020                         }
14021                 }
14022                 return false;
14023         }
14024    };   
14025 }();/*
14026  * Based on:
14027  * Ext JS Library 1.1.1
14028  * Copyright(c) 2006-2007, Ext JS, LLC.
14029  *
14030  * Originally Released Under LGPL - original licence link has changed is not relivant.
14031  *
14032  * Fork - LGPL
14033  * <script type="text/javascript">
14034  */
14035
14036  
14037
14038 /**
14039  * @class Roo.util.ClickRepeater
14040  * @extends Roo.util.Observable
14041  * 
14042  * A wrapper class which can be applied to any element. Fires a "click" event while the
14043  * mouse is pressed. The interval between firings may be specified in the config but
14044  * defaults to 10 milliseconds.
14045  * 
14046  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14047  * 
14048  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14049  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14050  * Similar to an autorepeat key delay.
14051  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14052  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14053  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14054  *           "interval" and "delay" are ignored. "immediate" is honored.
14055  * @cfg {Boolean} preventDefault True to prevent the default click event
14056  * @cfg {Boolean} stopDefault True to stop the default click event
14057  * 
14058  * @history
14059  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14060  *     2007-02-02 jvs Renamed to ClickRepeater
14061  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14062  *
14063  *  @constructor
14064  * @param {String/HTMLElement/Element} el The element to listen on
14065  * @param {Object} config
14066  **/
14067 Roo.util.ClickRepeater = function(el, config)
14068 {
14069     this.el = Roo.get(el);
14070     this.el.unselectable();
14071
14072     Roo.apply(this, config);
14073
14074     this.addEvents({
14075     /**
14076      * @event mousedown
14077      * Fires when the mouse button is depressed.
14078      * @param {Roo.util.ClickRepeater} this
14079      */
14080         "mousedown" : true,
14081     /**
14082      * @event click
14083      * Fires on a specified interval during the time the element is pressed.
14084      * @param {Roo.util.ClickRepeater} this
14085      */
14086         "click" : true,
14087     /**
14088      * @event mouseup
14089      * Fires when the mouse key is released.
14090      * @param {Roo.util.ClickRepeater} this
14091      */
14092         "mouseup" : true
14093     });
14094
14095     this.el.on("mousedown", this.handleMouseDown, this);
14096     if(this.preventDefault || this.stopDefault){
14097         this.el.on("click", function(e){
14098             if(this.preventDefault){
14099                 e.preventDefault();
14100             }
14101             if(this.stopDefault){
14102                 e.stopEvent();
14103             }
14104         }, this);
14105     }
14106
14107     // allow inline handler
14108     if(this.handler){
14109         this.on("click", this.handler,  this.scope || this);
14110     }
14111
14112     Roo.util.ClickRepeater.superclass.constructor.call(this);
14113 };
14114
14115 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14116     interval : 20,
14117     delay: 250,
14118     preventDefault : true,
14119     stopDefault : false,
14120     timer : 0,
14121
14122     // private
14123     handleMouseDown : function(){
14124         clearTimeout(this.timer);
14125         this.el.blur();
14126         if(this.pressClass){
14127             this.el.addClass(this.pressClass);
14128         }
14129         this.mousedownTime = new Date();
14130
14131         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14132         this.el.on("mouseout", this.handleMouseOut, this);
14133
14134         this.fireEvent("mousedown", this);
14135         this.fireEvent("click", this);
14136         
14137         this.timer = this.click.defer(this.delay || this.interval, this);
14138     },
14139
14140     // private
14141     click : function(){
14142         this.fireEvent("click", this);
14143         this.timer = this.click.defer(this.getInterval(), this);
14144     },
14145
14146     // private
14147     getInterval: function(){
14148         if(!this.accelerate){
14149             return this.interval;
14150         }
14151         var pressTime = this.mousedownTime.getElapsed();
14152         if(pressTime < 500){
14153             return 400;
14154         }else if(pressTime < 1700){
14155             return 320;
14156         }else if(pressTime < 2600){
14157             return 250;
14158         }else if(pressTime < 3500){
14159             return 180;
14160         }else if(pressTime < 4400){
14161             return 140;
14162         }else if(pressTime < 5300){
14163             return 80;
14164         }else if(pressTime < 6200){
14165             return 50;
14166         }else{
14167             return 10;
14168         }
14169     },
14170
14171     // private
14172     handleMouseOut : function(){
14173         clearTimeout(this.timer);
14174         if(this.pressClass){
14175             this.el.removeClass(this.pressClass);
14176         }
14177         this.el.on("mouseover", this.handleMouseReturn, this);
14178     },
14179
14180     // private
14181     handleMouseReturn : function(){
14182         this.el.un("mouseover", this.handleMouseReturn);
14183         if(this.pressClass){
14184             this.el.addClass(this.pressClass);
14185         }
14186         this.click();
14187     },
14188
14189     // private
14190     handleMouseUp : function(){
14191         clearTimeout(this.timer);
14192         this.el.un("mouseover", this.handleMouseReturn);
14193         this.el.un("mouseout", this.handleMouseOut);
14194         Roo.get(document).un("mouseup", this.handleMouseUp);
14195         this.el.removeClass(this.pressClass);
14196         this.fireEvent("mouseup", this);
14197     }
14198 });/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209  
14210 /**
14211  * @class Roo.KeyNav
14212  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14213  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14214  * way to implement custom navigation schemes for any UI component.</p>
14215  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14216  * pageUp, pageDown, del, home, end.  Usage:</p>
14217  <pre><code>
14218 var nav = new Roo.KeyNav("my-element", {
14219     "left" : function(e){
14220         this.moveLeft(e.ctrlKey);
14221     },
14222     "right" : function(e){
14223         this.moveRight(e.ctrlKey);
14224     },
14225     "enter" : function(e){
14226         this.save();
14227     },
14228     scope : this
14229 });
14230 </code></pre>
14231  * @constructor
14232  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14233  * @param {Object} config The config
14234  */
14235 Roo.KeyNav = function(el, config){
14236     this.el = Roo.get(el);
14237     Roo.apply(this, config);
14238     if(!this.disabled){
14239         this.disabled = true;
14240         this.enable();
14241     }
14242 };
14243
14244 Roo.KeyNav.prototype = {
14245     /**
14246      * @cfg {Boolean} disabled
14247      * True to disable this KeyNav instance (defaults to false)
14248      */
14249     disabled : false,
14250     /**
14251      * @cfg {String} defaultEventAction
14252      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14253      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14254      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14255      */
14256     defaultEventAction: "stopEvent",
14257     /**
14258      * @cfg {Boolean} forceKeyDown
14259      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14260      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14261      * handle keydown instead of keypress.
14262      */
14263     forceKeyDown : false,
14264
14265     // private
14266     prepareEvent : function(e){
14267         var k = e.getKey();
14268         var h = this.keyToHandler[k];
14269         //if(h && this[h]){
14270         //    e.stopPropagation();
14271         //}
14272         if(Roo.isSafari && h && k >= 37 && k <= 40){
14273             e.stopEvent();
14274         }
14275     },
14276
14277     // private
14278     relay : function(e){
14279         var k = e.getKey();
14280         var h = this.keyToHandler[k];
14281         if(h && this[h]){
14282             if(this.doRelay(e, this[h], h) !== true){
14283                 e[this.defaultEventAction]();
14284             }
14285         }
14286     },
14287
14288     // private
14289     doRelay : function(e, h, hname){
14290         return h.call(this.scope || this, e);
14291     },
14292
14293     // possible handlers
14294     enter : false,
14295     left : false,
14296     right : false,
14297     up : false,
14298     down : false,
14299     tab : false,
14300     esc : false,
14301     pageUp : false,
14302     pageDown : false,
14303     del : false,
14304     home : false,
14305     end : false,
14306
14307     // quick lookup hash
14308     keyToHandler : {
14309         37 : "left",
14310         39 : "right",
14311         38 : "up",
14312         40 : "down",
14313         33 : "pageUp",
14314         34 : "pageDown",
14315         46 : "del",
14316         36 : "home",
14317         35 : "end",
14318         13 : "enter",
14319         27 : "esc",
14320         9  : "tab"
14321     },
14322
14323         /**
14324          * Enable this KeyNav
14325          */
14326         enable: function(){
14327                 if(this.disabled){
14328             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14329             // the EventObject will normalize Safari automatically
14330             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14331                 this.el.on("keydown", this.relay,  this);
14332             }else{
14333                 this.el.on("keydown", this.prepareEvent,  this);
14334                 this.el.on("keypress", this.relay,  this);
14335             }
14336                     this.disabled = false;
14337                 }
14338         },
14339
14340         /**
14341          * Disable this KeyNav
14342          */
14343         disable: function(){
14344                 if(!this.disabled){
14345                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14346                 this.el.un("keydown", this.relay);
14347             }else{
14348                 this.el.un("keydown", this.prepareEvent);
14349                 this.el.un("keypress", this.relay);
14350             }
14351                     this.disabled = true;
14352                 }
14353         }
14354 };/*
14355  * Based on:
14356  * Ext JS Library 1.1.1
14357  * Copyright(c) 2006-2007, Ext JS, LLC.
14358  *
14359  * Originally Released Under LGPL - original licence link has changed is not relivant.
14360  *
14361  * Fork - LGPL
14362  * <script type="text/javascript">
14363  */
14364
14365  
14366 /**
14367  * @class Roo.KeyMap
14368  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14369  * The constructor accepts the same config object as defined by {@link #addBinding}.
14370  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14371  * combination it will call the function with this signature (if the match is a multi-key
14372  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14373  * A KeyMap can also handle a string representation of keys.<br />
14374  * Usage:
14375  <pre><code>
14376 // map one key by key code
14377 var map = new Roo.KeyMap("my-element", {
14378     key: 13, // or Roo.EventObject.ENTER
14379     fn: myHandler,
14380     scope: myObject
14381 });
14382
14383 // map multiple keys to one action by string
14384 var map = new Roo.KeyMap("my-element", {
14385     key: "a\r\n\t",
14386     fn: myHandler,
14387     scope: myObject
14388 });
14389
14390 // map multiple keys to multiple actions by strings and array of codes
14391 var map = new Roo.KeyMap("my-element", [
14392     {
14393         key: [10,13],
14394         fn: function(){ alert("Return was pressed"); }
14395     }, {
14396         key: "abc",
14397         fn: function(){ alert('a, b or c was pressed'); }
14398     }, {
14399         key: "\t",
14400         ctrl:true,
14401         shift:true,
14402         fn: function(){ alert('Control + shift + tab was pressed.'); }
14403     }
14404 ]);
14405 </code></pre>
14406  * <b>Note: A KeyMap starts enabled</b>
14407  * @constructor
14408  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14409  * @param {Object} config The config (see {@link #addBinding})
14410  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14411  */
14412 Roo.KeyMap = function(el, config, eventName){
14413     this.el  = Roo.get(el);
14414     this.eventName = eventName || "keydown";
14415     this.bindings = [];
14416     if(config){
14417         this.addBinding(config);
14418     }
14419     this.enable();
14420 };
14421
14422 Roo.KeyMap.prototype = {
14423     /**
14424      * True to stop the event from bubbling and prevent the default browser action if the
14425      * key was handled by the KeyMap (defaults to false)
14426      * @type Boolean
14427      */
14428     stopEvent : false,
14429
14430     /**
14431      * Add a new binding to this KeyMap. The following config object properties are supported:
14432      * <pre>
14433 Property    Type             Description
14434 ----------  ---------------  ----------------------------------------------------------------------
14435 key         String/Array     A single keycode or an array of keycodes to handle
14436 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14437 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14438 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14439 fn          Function         The function to call when KeyMap finds the expected key combination
14440 scope       Object           The scope of the callback function
14441 </pre>
14442      *
14443      * Usage:
14444      * <pre><code>
14445 // Create a KeyMap
14446 var map = new Roo.KeyMap(document, {
14447     key: Roo.EventObject.ENTER,
14448     fn: handleKey,
14449     scope: this
14450 });
14451
14452 //Add a new binding to the existing KeyMap later
14453 map.addBinding({
14454     key: 'abc',
14455     shift: true,
14456     fn: handleKey,
14457     scope: this
14458 });
14459 </code></pre>
14460      * @param {Object/Array} config A single KeyMap config or an array of configs
14461      */
14462         addBinding : function(config){
14463         if(config instanceof Array){
14464             for(var i = 0, len = config.length; i < len; i++){
14465                 this.addBinding(config[i]);
14466             }
14467             return;
14468         }
14469         var keyCode = config.key,
14470             shift = config.shift, 
14471             ctrl = config.ctrl, 
14472             alt = config.alt,
14473             fn = config.fn,
14474             scope = config.scope;
14475         if(typeof keyCode == "string"){
14476             var ks = [];
14477             var keyString = keyCode.toUpperCase();
14478             for(var j = 0, len = keyString.length; j < len; j++){
14479                 ks.push(keyString.charCodeAt(j));
14480             }
14481             keyCode = ks;
14482         }
14483         var keyArray = keyCode instanceof Array;
14484         var handler = function(e){
14485             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14486                 var k = e.getKey();
14487                 if(keyArray){
14488                     for(var i = 0, len = keyCode.length; i < len; i++){
14489                         if(keyCode[i] == k){
14490                           if(this.stopEvent){
14491                               e.stopEvent();
14492                           }
14493                           fn.call(scope || window, k, e);
14494                           return;
14495                         }
14496                     }
14497                 }else{
14498                     if(k == keyCode){
14499                         if(this.stopEvent){
14500                            e.stopEvent();
14501                         }
14502                         fn.call(scope || window, k, e);
14503                     }
14504                 }
14505             }
14506         };
14507         this.bindings.push(handler);  
14508         },
14509
14510     /**
14511      * Shorthand for adding a single key listener
14512      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14513      * following options:
14514      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14515      * @param {Function} fn The function to call
14516      * @param {Object} scope (optional) The scope of the function
14517      */
14518     on : function(key, fn, scope){
14519         var keyCode, shift, ctrl, alt;
14520         if(typeof key == "object" && !(key instanceof Array)){
14521             keyCode = key.key;
14522             shift = key.shift;
14523             ctrl = key.ctrl;
14524             alt = key.alt;
14525         }else{
14526             keyCode = key;
14527         }
14528         this.addBinding({
14529             key: keyCode,
14530             shift: shift,
14531             ctrl: ctrl,
14532             alt: alt,
14533             fn: fn,
14534             scope: scope
14535         })
14536     },
14537
14538     // private
14539     handleKeyDown : function(e){
14540             if(this.enabled){ //just in case
14541             var b = this.bindings;
14542             for(var i = 0, len = b.length; i < len; i++){
14543                 b[i].call(this, e);
14544             }
14545             }
14546         },
14547         
14548         /**
14549          * Returns true if this KeyMap is enabled
14550          * @return {Boolean} 
14551          */
14552         isEnabled : function(){
14553             return this.enabled;  
14554         },
14555         
14556         /**
14557          * Enables this KeyMap
14558          */
14559         enable: function(){
14560                 if(!this.enabled){
14561                     this.el.on(this.eventName, this.handleKeyDown, this);
14562                     this.enabled = true;
14563                 }
14564         },
14565
14566         /**
14567          * Disable this KeyMap
14568          */
14569         disable: function(){
14570                 if(this.enabled){
14571                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14572                     this.enabled = false;
14573                 }
14574         }
14575 };/*
14576  * Based on:
14577  * Ext JS Library 1.1.1
14578  * Copyright(c) 2006-2007, Ext JS, LLC.
14579  *
14580  * Originally Released Under LGPL - original licence link has changed is not relivant.
14581  *
14582  * Fork - LGPL
14583  * <script type="text/javascript">
14584  */
14585
14586  
14587 /**
14588  * @class Roo.util.TextMetrics
14589  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14590  * wide, in pixels, a given block of text will be.
14591  * @singleton
14592  */
14593 Roo.util.TextMetrics = function(){
14594     var shared;
14595     return {
14596         /**
14597          * Measures the size of the specified text
14598          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14599          * that can affect the size of the rendered text
14600          * @param {String} text The text to measure
14601          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14602          * in order to accurately measure the text height
14603          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14604          */
14605         measure : function(el, text, fixedWidth){
14606             if(!shared){
14607                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14608             }
14609             shared.bind(el);
14610             shared.setFixedWidth(fixedWidth || 'auto');
14611             return shared.getSize(text);
14612         },
14613
14614         /**
14615          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14616          * the overhead of multiple calls to initialize the style properties on each measurement.
14617          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14618          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14619          * in order to accurately measure the text height
14620          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14621          */
14622         createInstance : function(el, fixedWidth){
14623             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14624         }
14625     };
14626 }();
14627
14628  
14629
14630 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14631     var ml = new Roo.Element(document.createElement('div'));
14632     document.body.appendChild(ml.dom);
14633     ml.position('absolute');
14634     ml.setLeftTop(-1000, -1000);
14635     ml.hide();
14636
14637     if(fixedWidth){
14638         ml.setWidth(fixedWidth);
14639     }
14640      
14641     var instance = {
14642         /**
14643          * Returns the size of the specified text based on the internal element's style and width properties
14644          * @memberOf Roo.util.TextMetrics.Instance#
14645          * @param {String} text The text to measure
14646          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14647          */
14648         getSize : function(text){
14649             ml.update(text);
14650             var s = ml.getSize();
14651             ml.update('');
14652             return s;
14653         },
14654
14655         /**
14656          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14657          * that can affect the size of the rendered text
14658          * @memberOf Roo.util.TextMetrics.Instance#
14659          * @param {String/HTMLElement} el The element, dom node or id
14660          */
14661         bind : function(el){
14662             ml.setStyle(
14663                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14664             );
14665         },
14666
14667         /**
14668          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14669          * to set a fixed width in order to accurately measure the text height.
14670          * @memberOf Roo.util.TextMetrics.Instance#
14671          * @param {Number} width The width to set on the element
14672          */
14673         setFixedWidth : function(width){
14674             ml.setWidth(width);
14675         },
14676
14677         /**
14678          * Returns the measured width of the specified text
14679          * @memberOf Roo.util.TextMetrics.Instance#
14680          * @param {String} text The text to measure
14681          * @return {Number} width The width in pixels
14682          */
14683         getWidth : function(text){
14684             ml.dom.style.width = 'auto';
14685             return this.getSize(text).width;
14686         },
14687
14688         /**
14689          * Returns the measured height of the specified text.  For multiline text, be sure to call
14690          * {@link #setFixedWidth} if necessary.
14691          * @memberOf Roo.util.TextMetrics.Instance#
14692          * @param {String} text The text to measure
14693          * @return {Number} height The height in pixels
14694          */
14695         getHeight : function(text){
14696             return this.getSize(text).height;
14697         }
14698     };
14699
14700     instance.bind(bindTo);
14701
14702     return instance;
14703 };
14704
14705 // backwards compat
14706 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14707  * Based on:
14708  * Ext JS Library 1.1.1
14709  * Copyright(c) 2006-2007, Ext JS, LLC.
14710  *
14711  * Originally Released Under LGPL - original licence link has changed is not relivant.
14712  *
14713  * Fork - LGPL
14714  * <script type="text/javascript">
14715  */
14716
14717 /**
14718  * @class Roo.state.Provider
14719  * Abstract base class for state provider implementations. This class provides methods
14720  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14721  * Provider interface.
14722  */
14723 Roo.state.Provider = function(){
14724     /**
14725      * @event statechange
14726      * Fires when a state change occurs.
14727      * @param {Provider} this This state provider
14728      * @param {String} key The state key which was changed
14729      * @param {String} value The encoded value for the state
14730      */
14731     this.addEvents({
14732         "statechange": true
14733     });
14734     this.state = {};
14735     Roo.state.Provider.superclass.constructor.call(this);
14736 };
14737 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14738     /**
14739      * Returns the current value for a key
14740      * @param {String} name The key name
14741      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14742      * @return {Mixed} The state data
14743      */
14744     get : function(name, defaultValue){
14745         return typeof this.state[name] == "undefined" ?
14746             defaultValue : this.state[name];
14747     },
14748     
14749     /**
14750      * Clears a value from the state
14751      * @param {String} name The key name
14752      */
14753     clear : function(name){
14754         delete this.state[name];
14755         this.fireEvent("statechange", this, name, null);
14756     },
14757     
14758     /**
14759      * Sets the value for a key
14760      * @param {String} name The key name
14761      * @param {Mixed} value The value to set
14762      */
14763     set : function(name, value){
14764         this.state[name] = value;
14765         this.fireEvent("statechange", this, name, value);
14766     },
14767     
14768     /**
14769      * Decodes a string previously encoded with {@link #encodeValue}.
14770      * @param {String} value The value to decode
14771      * @return {Mixed} The decoded value
14772      */
14773     decodeValue : function(cookie){
14774         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14775         var matches = re.exec(unescape(cookie));
14776         if(!matches || !matches[1]) return; // non state cookie
14777         var type = matches[1];
14778         var v = matches[2];
14779         switch(type){
14780             case "n":
14781                 return parseFloat(v);
14782             case "d":
14783                 return new Date(Date.parse(v));
14784             case "b":
14785                 return (v == "1");
14786             case "a":
14787                 var all = [];
14788                 var values = v.split("^");
14789                 for(var i = 0, len = values.length; i < len; i++){
14790                     all.push(this.decodeValue(values[i]));
14791                 }
14792                 return all;
14793            case "o":
14794                 var all = {};
14795                 var values = v.split("^");
14796                 for(var i = 0, len = values.length; i < len; i++){
14797                     var kv = values[i].split("=");
14798                     all[kv[0]] = this.decodeValue(kv[1]);
14799                 }
14800                 return all;
14801            default:
14802                 return v;
14803         }
14804     },
14805     
14806     /**
14807      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14808      * @param {Mixed} value The value to encode
14809      * @return {String} The encoded value
14810      */
14811     encodeValue : function(v){
14812         var enc;
14813         if(typeof v == "number"){
14814             enc = "n:" + v;
14815         }else if(typeof v == "boolean"){
14816             enc = "b:" + (v ? "1" : "0");
14817         }else if(v instanceof Date){
14818             enc = "d:" + v.toGMTString();
14819         }else if(v instanceof Array){
14820             var flat = "";
14821             for(var i = 0, len = v.length; i < len; i++){
14822                 flat += this.encodeValue(v[i]);
14823                 if(i != len-1) flat += "^";
14824             }
14825             enc = "a:" + flat;
14826         }else if(typeof v == "object"){
14827             var flat = "";
14828             for(var key in v){
14829                 if(typeof v[key] != "function"){
14830                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14831                 }
14832             }
14833             enc = "o:" + flat.substring(0, flat.length-1);
14834         }else{
14835             enc = "s:" + v;
14836         }
14837         return escape(enc);        
14838     }
14839 });
14840
14841 /*
14842  * Based on:
14843  * Ext JS Library 1.1.1
14844  * Copyright(c) 2006-2007, Ext JS, LLC.
14845  *
14846  * Originally Released Under LGPL - original licence link has changed is not relivant.
14847  *
14848  * Fork - LGPL
14849  * <script type="text/javascript">
14850  */
14851 /**
14852  * @class Roo.state.Manager
14853  * This is the global state manager. By default all components that are "state aware" check this class
14854  * for state information if you don't pass them a custom state provider. In order for this class
14855  * to be useful, it must be initialized with a provider when your application initializes.
14856  <pre><code>
14857 // in your initialization function
14858 init : function(){
14859    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14860    ...
14861    // supposed you have a {@link Roo.BorderLayout}
14862    var layout = new Roo.BorderLayout(...);
14863    layout.restoreState();
14864    // or a {Roo.BasicDialog}
14865    var dialog = new Roo.BasicDialog(...);
14866    dialog.restoreState();
14867  </code></pre>
14868  * @singleton
14869  */
14870 Roo.state.Manager = function(){
14871     var provider = new Roo.state.Provider();
14872     
14873     return {
14874         /**
14875          * Configures the default state provider for your application
14876          * @param {Provider} stateProvider The state provider to set
14877          */
14878         setProvider : function(stateProvider){
14879             provider = stateProvider;
14880         },
14881         
14882         /**
14883          * Returns the current value for a key
14884          * @param {String} name The key name
14885          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14886          * @return {Mixed} The state data
14887          */
14888         get : function(key, defaultValue){
14889             return provider.get(key, defaultValue);
14890         },
14891         
14892         /**
14893          * Sets the value for a key
14894          * @param {String} name The key name
14895          * @param {Mixed} value The state data
14896          */
14897          set : function(key, value){
14898             provider.set(key, value);
14899         },
14900         
14901         /**
14902          * Clears a value from the state
14903          * @param {String} name The key name
14904          */
14905         clear : function(key){
14906             provider.clear(key);
14907         },
14908         
14909         /**
14910          * Gets the currently configured state provider
14911          * @return {Provider} The state provider
14912          */
14913         getProvider : function(){
14914             return provider;
14915         }
14916     };
14917 }();
14918 /*
14919  * Based on:
14920  * Ext JS Library 1.1.1
14921  * Copyright(c) 2006-2007, Ext JS, LLC.
14922  *
14923  * Originally Released Under LGPL - original licence link has changed is not relivant.
14924  *
14925  * Fork - LGPL
14926  * <script type="text/javascript">
14927  */
14928 /**
14929  * @class Roo.state.CookieProvider
14930  * @extends Roo.state.Provider
14931  * The default Provider implementation which saves state via cookies.
14932  * <br />Usage:
14933  <pre><code>
14934    var cp = new Roo.state.CookieProvider({
14935        path: "/cgi-bin/",
14936        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14937        domain: "roojs.com"
14938    })
14939    Roo.state.Manager.setProvider(cp);
14940  </code></pre>
14941  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14942  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14943  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14944  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14945  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14946  * domain the page is running on including the 'www' like 'www.roojs.com')
14947  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14948  * @constructor
14949  * Create a new CookieProvider
14950  * @param {Object} config The configuration object
14951  */
14952 Roo.state.CookieProvider = function(config){
14953     Roo.state.CookieProvider.superclass.constructor.call(this);
14954     this.path = "/";
14955     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14956     this.domain = null;
14957     this.secure = false;
14958     Roo.apply(this, config);
14959     this.state = this.readCookies();
14960 };
14961
14962 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14963     // private
14964     set : function(name, value){
14965         if(typeof value == "undefined" || value === null){
14966             this.clear(name);
14967             return;
14968         }
14969         this.setCookie(name, value);
14970         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14971     },
14972
14973     // private
14974     clear : function(name){
14975         this.clearCookie(name);
14976         Roo.state.CookieProvider.superclass.clear.call(this, name);
14977     },
14978
14979     // private
14980     readCookies : function(){
14981         var cookies = {};
14982         var c = document.cookie + ";";
14983         var re = /\s?(.*?)=(.*?);/g;
14984         var matches;
14985         while((matches = re.exec(c)) != null){
14986             var name = matches[1];
14987             var value = matches[2];
14988             if(name && name.substring(0,3) == "ys-"){
14989                 cookies[name.substr(3)] = this.decodeValue(value);
14990             }
14991         }
14992         return cookies;
14993     },
14994
14995     // private
14996     setCookie : function(name, value){
14997         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14998            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14999            ((this.path == null) ? "" : ("; path=" + this.path)) +
15000            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15001            ((this.secure == true) ? "; secure" : "");
15002     },
15003
15004     // private
15005     clearCookie : function(name){
15006         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15007            ((this.path == null) ? "" : ("; path=" + this.path)) +
15008            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15009            ((this.secure == true) ? "; secure" : "");
15010     }
15011 });/*
15012  * Based on:
15013  * Ext JS Library 1.1.1
15014  * Copyright(c) 2006-2007, Ext JS, LLC.
15015  *
15016  * Originally Released Under LGPL - original licence link has changed is not relivant.
15017  *
15018  * Fork - LGPL
15019  * <script type="text/javascript">
15020  */
15021  
15022
15023 /**
15024  * @class Roo.ComponentMgr
15025  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15026  * @singleton
15027  */
15028 Roo.ComponentMgr = function(){
15029     var all = new Roo.util.MixedCollection();
15030
15031     return {
15032         /**
15033          * Registers a component.
15034          * @param {Roo.Component} c The component
15035          */
15036         register : function(c){
15037             all.add(c);
15038         },
15039
15040         /**
15041          * Unregisters a component.
15042          * @param {Roo.Component} c The component
15043          */
15044         unregister : function(c){
15045             all.remove(c);
15046         },
15047
15048         /**
15049          * Returns a component by id
15050          * @param {String} id The component id
15051          */
15052         get : function(id){
15053             return all.get(id);
15054         },
15055
15056         /**
15057          * Registers a function that will be called when a specified component is added to ComponentMgr
15058          * @param {String} id The component id
15059          * @param {Funtction} fn The callback function
15060          * @param {Object} scope The scope of the callback
15061          */
15062         onAvailable : function(id, fn, scope){
15063             all.on("add", function(index, o){
15064                 if(o.id == id){
15065                     fn.call(scope || o, o);
15066                     all.un("add", fn, scope);
15067                 }
15068             });
15069         }
15070     };
15071 }();/*
15072  * Based on:
15073  * Ext JS Library 1.1.1
15074  * Copyright(c) 2006-2007, Ext JS, LLC.
15075  *
15076  * Originally Released Under LGPL - original licence link has changed is not relivant.
15077  *
15078  * Fork - LGPL
15079  * <script type="text/javascript">
15080  */
15081  
15082 /**
15083  * @class Roo.Component
15084  * @extends Roo.util.Observable
15085  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15086  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15087  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15088  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15089  * All visual components (widgets) that require rendering into a layout should subclass Component.
15090  * @constructor
15091  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15092  * 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
15093  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15094  */
15095 Roo.Component = function(config){
15096     config = config || {};
15097     if(config.tagName || config.dom || typeof config == "string"){ // element object
15098         config = {el: config, id: config.id || config};
15099     }
15100     this.initialConfig = config;
15101
15102     Roo.apply(this, config);
15103     this.addEvents({
15104         /**
15105          * @event disable
15106          * Fires after the component is disabled.
15107              * @param {Roo.Component} this
15108              */
15109         disable : true,
15110         /**
15111          * @event enable
15112          * Fires after the component is enabled.
15113              * @param {Roo.Component} this
15114              */
15115         enable : true,
15116         /**
15117          * @event beforeshow
15118          * Fires before the component is shown.  Return false to stop the show.
15119              * @param {Roo.Component} this
15120              */
15121         beforeshow : true,
15122         /**
15123          * @event show
15124          * Fires after the component is shown.
15125              * @param {Roo.Component} this
15126              */
15127         show : true,
15128         /**
15129          * @event beforehide
15130          * Fires before the component is hidden. Return false to stop the hide.
15131              * @param {Roo.Component} this
15132              */
15133         beforehide : true,
15134         /**
15135          * @event hide
15136          * Fires after the component is hidden.
15137              * @param {Roo.Component} this
15138              */
15139         hide : true,
15140         /**
15141          * @event beforerender
15142          * Fires before the component is rendered. Return false to stop the render.
15143              * @param {Roo.Component} this
15144              */
15145         beforerender : true,
15146         /**
15147          * @event render
15148          * Fires after the component is rendered.
15149              * @param {Roo.Component} this
15150              */
15151         render : true,
15152         /**
15153          * @event beforedestroy
15154          * Fires before the component is destroyed. Return false to stop the destroy.
15155              * @param {Roo.Component} this
15156              */
15157         beforedestroy : true,
15158         /**
15159          * @event destroy
15160          * Fires after the component is destroyed.
15161              * @param {Roo.Component} this
15162              */
15163         destroy : true
15164     });
15165     if(!this.id){
15166         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15167     }
15168     Roo.ComponentMgr.register(this);
15169     Roo.Component.superclass.constructor.call(this);
15170     this.initComponent();
15171     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15172         this.render(this.renderTo);
15173         delete this.renderTo;
15174     }
15175 };
15176
15177 /** @private */
15178 Roo.Component.AUTO_ID = 1000;
15179
15180 Roo.extend(Roo.Component, Roo.util.Observable, {
15181     /**
15182      * @scope Roo.Component.prototype
15183      * @type {Boolean}
15184      * true if this component is hidden. Read-only.
15185      */
15186     hidden : false,
15187     /**
15188      * @type {Boolean}
15189      * true if this component is disabled. Read-only.
15190      */
15191     disabled : false,
15192     /**
15193      * @type {Boolean}
15194      * true if this component has been rendered. Read-only.
15195      */
15196     rendered : false,
15197     
15198     /** @cfg {String} disableClass
15199      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15200      */
15201     disabledClass : "x-item-disabled",
15202         /** @cfg {Boolean} allowDomMove
15203          * Whether the component can move the Dom node when rendering (defaults to true).
15204          */
15205     allowDomMove : true,
15206     /** @cfg {String} hideMode
15207      * How this component should hidden. Supported values are
15208      * "visibility" (css visibility), "offsets" (negative offset position) and
15209      * "display" (css display) - defaults to "display".
15210      */
15211     hideMode: 'display',
15212
15213     /** @private */
15214     ctype : "Roo.Component",
15215
15216     /**
15217      * @cfg {String} actionMode 
15218      * which property holds the element that used for  hide() / show() / disable() / enable()
15219      * default is 'el' 
15220      */
15221     actionMode : "el",
15222
15223     /** @private */
15224     getActionEl : function(){
15225         return this[this.actionMode];
15226     },
15227
15228     initComponent : Roo.emptyFn,
15229     /**
15230      * If this is a lazy rendering component, render it to its container element.
15231      * @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.
15232      */
15233     render : function(container, position){
15234         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15235             if(!container && this.el){
15236                 this.el = Roo.get(this.el);
15237                 container = this.el.dom.parentNode;
15238                 this.allowDomMove = false;
15239             }
15240             this.container = Roo.get(container);
15241             this.rendered = true;
15242             if(position !== undefined){
15243                 if(typeof position == 'number'){
15244                     position = this.container.dom.childNodes[position];
15245                 }else{
15246                     position = Roo.getDom(position);
15247                 }
15248             }
15249             this.onRender(this.container, position || null);
15250             if(this.cls){
15251                 this.el.addClass(this.cls);
15252                 delete this.cls;
15253             }
15254             if(this.style){
15255                 this.el.applyStyles(this.style);
15256                 delete this.style;
15257             }
15258             this.fireEvent("render", this);
15259             this.afterRender(this.container);
15260             if(this.hidden){
15261                 this.hide();
15262             }
15263             if(this.disabled){
15264                 this.disable();
15265             }
15266         }
15267         return this;
15268     },
15269
15270     /** @private */
15271     // default function is not really useful
15272     onRender : function(ct, position){
15273         if(this.el){
15274             this.el = Roo.get(this.el);
15275             if(this.allowDomMove !== false){
15276                 ct.dom.insertBefore(this.el.dom, position);
15277             }
15278         }
15279     },
15280
15281     /** @private */
15282     getAutoCreate : function(){
15283         var cfg = typeof this.autoCreate == "object" ?
15284                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15285         if(this.id && !cfg.id){
15286             cfg.id = this.id;
15287         }
15288         return cfg;
15289     },
15290
15291     /** @private */
15292     afterRender : Roo.emptyFn,
15293
15294     /**
15295      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15296      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15297      */
15298     destroy : function(){
15299         if(this.fireEvent("beforedestroy", this) !== false){
15300             this.purgeListeners();
15301             this.beforeDestroy();
15302             if(this.rendered){
15303                 this.el.removeAllListeners();
15304                 this.el.remove();
15305                 if(this.actionMode == "container"){
15306                     this.container.remove();
15307                 }
15308             }
15309             this.onDestroy();
15310             Roo.ComponentMgr.unregister(this);
15311             this.fireEvent("destroy", this);
15312         }
15313     },
15314
15315         /** @private */
15316     beforeDestroy : function(){
15317
15318     },
15319
15320         /** @private */
15321         onDestroy : function(){
15322
15323     },
15324
15325     /**
15326      * Returns the underlying {@link Roo.Element}.
15327      * @return {Roo.Element} The element
15328      */
15329     getEl : function(){
15330         return this.el;
15331     },
15332
15333     /**
15334      * Returns the id of this component.
15335      * @return {String}
15336      */
15337     getId : function(){
15338         return this.id;
15339     },
15340
15341     /**
15342      * Try to focus this component.
15343      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15344      * @return {Roo.Component} this
15345      */
15346     focus : function(selectText){
15347         if(this.rendered){
15348             this.el.focus();
15349             if(selectText === true){
15350                 this.el.dom.select();
15351             }
15352         }
15353         return this;
15354     },
15355
15356     /** @private */
15357     blur : function(){
15358         if(this.rendered){
15359             this.el.blur();
15360         }
15361         return this;
15362     },
15363
15364     /**
15365      * Disable this component.
15366      * @return {Roo.Component} this
15367      */
15368     disable : function(){
15369         if(this.rendered){
15370             this.onDisable();
15371         }
15372         this.disabled = true;
15373         this.fireEvent("disable", this);
15374         return this;
15375     },
15376
15377         // private
15378     onDisable : function(){
15379         this.getActionEl().addClass(this.disabledClass);
15380         this.el.dom.disabled = true;
15381     },
15382
15383     /**
15384      * Enable this component.
15385      * @return {Roo.Component} this
15386      */
15387     enable : function(){
15388         if(this.rendered){
15389             this.onEnable();
15390         }
15391         this.disabled = false;
15392         this.fireEvent("enable", this);
15393         return this;
15394     },
15395
15396         // private
15397     onEnable : function(){
15398         this.getActionEl().removeClass(this.disabledClass);
15399         this.el.dom.disabled = false;
15400     },
15401
15402     /**
15403      * Convenience function for setting disabled/enabled by boolean.
15404      * @param {Boolean} disabled
15405      */
15406     setDisabled : function(disabled){
15407         this[disabled ? "disable" : "enable"]();
15408     },
15409
15410     /**
15411      * Show this component.
15412      * @return {Roo.Component} this
15413      */
15414     show: function(){
15415         if(this.fireEvent("beforeshow", this) !== false){
15416             this.hidden = false;
15417             if(this.rendered){
15418                 this.onShow();
15419             }
15420             this.fireEvent("show", this);
15421         }
15422         return this;
15423     },
15424
15425     // private
15426     onShow : function(){
15427         var ae = this.getActionEl();
15428         if(this.hideMode == 'visibility'){
15429             ae.dom.style.visibility = "visible";
15430         }else if(this.hideMode == 'offsets'){
15431             ae.removeClass('x-hidden');
15432         }else{
15433             ae.dom.style.display = "";
15434         }
15435     },
15436
15437     /**
15438      * Hide this component.
15439      * @return {Roo.Component} this
15440      */
15441     hide: function(){
15442         if(this.fireEvent("beforehide", this) !== false){
15443             this.hidden = true;
15444             if(this.rendered){
15445                 this.onHide();
15446             }
15447             this.fireEvent("hide", this);
15448         }
15449         return this;
15450     },
15451
15452     // private
15453     onHide : function(){
15454         var ae = this.getActionEl();
15455         if(this.hideMode == 'visibility'){
15456             ae.dom.style.visibility = "hidden";
15457         }else if(this.hideMode == 'offsets'){
15458             ae.addClass('x-hidden');
15459         }else{
15460             ae.dom.style.display = "none";
15461         }
15462     },
15463
15464     /**
15465      * Convenience function to hide or show this component by boolean.
15466      * @param {Boolean} visible True to show, false to hide
15467      * @return {Roo.Component} this
15468      */
15469     setVisible: function(visible){
15470         if(visible) {
15471             this.show();
15472         }else{
15473             this.hide();
15474         }
15475         return this;
15476     },
15477
15478     /**
15479      * Returns true if this component is visible.
15480      */
15481     isVisible : function(){
15482         return this.getActionEl().isVisible();
15483     },
15484
15485     cloneConfig : function(overrides){
15486         overrides = overrides || {};
15487         var id = overrides.id || Roo.id();
15488         var cfg = Roo.applyIf(overrides, this.initialConfig);
15489         cfg.id = id; // prevent dup id
15490         return new this.constructor(cfg);
15491     }
15492 });/*
15493  * Based on:
15494  * Ext JS Library 1.1.1
15495  * Copyright(c) 2006-2007, Ext JS, LLC.
15496  *
15497  * Originally Released Under LGPL - original licence link has changed is not relivant.
15498  *
15499  * Fork - LGPL
15500  * <script type="text/javascript">
15501  */
15502
15503 /**
15504  * @class Roo.BoxComponent
15505  * @extends Roo.Component
15506  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15507  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15508  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15509  * layout containers.
15510  * @constructor
15511  * @param {Roo.Element/String/Object} config The configuration options.
15512  */
15513 Roo.BoxComponent = function(config){
15514     Roo.Component.call(this, config);
15515     this.addEvents({
15516         /**
15517          * @event resize
15518          * Fires after the component is resized.
15519              * @param {Roo.Component} this
15520              * @param {Number} adjWidth The box-adjusted width that was set
15521              * @param {Number} adjHeight The box-adjusted height that was set
15522              * @param {Number} rawWidth The width that was originally specified
15523              * @param {Number} rawHeight The height that was originally specified
15524              */
15525         resize : true,
15526         /**
15527          * @event move
15528          * Fires after the component is moved.
15529              * @param {Roo.Component} this
15530              * @param {Number} x The new x position
15531              * @param {Number} y The new y position
15532              */
15533         move : true
15534     });
15535 };
15536
15537 Roo.extend(Roo.BoxComponent, Roo.Component, {
15538     // private, set in afterRender to signify that the component has been rendered
15539     boxReady : false,
15540     // private, used to defer height settings to subclasses
15541     deferHeight: false,
15542     /** @cfg {Number} width
15543      * width (optional) size of component
15544      */
15545      /** @cfg {Number} height
15546      * height (optional) size of component
15547      */
15548      
15549     /**
15550      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15551      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15552      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15553      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15554      * @return {Roo.BoxComponent} this
15555      */
15556     setSize : function(w, h){
15557         // support for standard size objects
15558         if(typeof w == 'object'){
15559             h = w.height;
15560             w = w.width;
15561         }
15562         // not rendered
15563         if(!this.boxReady){
15564             this.width = w;
15565             this.height = h;
15566             return this;
15567         }
15568
15569         // prevent recalcs when not needed
15570         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15571             return this;
15572         }
15573         this.lastSize = {width: w, height: h};
15574
15575         var adj = this.adjustSize(w, h);
15576         var aw = adj.width, ah = adj.height;
15577         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15578             var rz = this.getResizeEl();
15579             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15580                 rz.setSize(aw, ah);
15581             }else if(!this.deferHeight && ah !== undefined){
15582                 rz.setHeight(ah);
15583             }else if(aw !== undefined){
15584                 rz.setWidth(aw);
15585             }
15586             this.onResize(aw, ah, w, h);
15587             this.fireEvent('resize', this, aw, ah, w, h);
15588         }
15589         return this;
15590     },
15591
15592     /**
15593      * Gets the current size of the component's underlying element.
15594      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15595      */
15596     getSize : function(){
15597         return this.el.getSize();
15598     },
15599
15600     /**
15601      * Gets the current XY position of the component's underlying element.
15602      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15603      * @return {Array} The XY position of the element (e.g., [100, 200])
15604      */
15605     getPosition : function(local){
15606         if(local === true){
15607             return [this.el.getLeft(true), this.el.getTop(true)];
15608         }
15609         return this.xy || this.el.getXY();
15610     },
15611
15612     /**
15613      * Gets the current box measurements of the component's underlying element.
15614      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15615      * @returns {Object} box An object in the format {x, y, width, height}
15616      */
15617     getBox : function(local){
15618         var s = this.el.getSize();
15619         if(local){
15620             s.x = this.el.getLeft(true);
15621             s.y = this.el.getTop(true);
15622         }else{
15623             var xy = this.xy || this.el.getXY();
15624             s.x = xy[0];
15625             s.y = xy[1];
15626         }
15627         return s;
15628     },
15629
15630     /**
15631      * Sets the current box measurements of the component's underlying element.
15632      * @param {Object} box An object in the format {x, y, width, height}
15633      * @returns {Roo.BoxComponent} this
15634      */
15635     updateBox : function(box){
15636         this.setSize(box.width, box.height);
15637         this.setPagePosition(box.x, box.y);
15638         return this;
15639     },
15640
15641     // protected
15642     getResizeEl : function(){
15643         return this.resizeEl || this.el;
15644     },
15645
15646     // protected
15647     getPositionEl : function(){
15648         return this.positionEl || this.el;
15649     },
15650
15651     /**
15652      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15653      * This method fires the move event.
15654      * @param {Number} left The new left
15655      * @param {Number} top The new top
15656      * @returns {Roo.BoxComponent} this
15657      */
15658     setPosition : function(x, y){
15659         this.x = x;
15660         this.y = y;
15661         if(!this.boxReady){
15662             return this;
15663         }
15664         var adj = this.adjustPosition(x, y);
15665         var ax = adj.x, ay = adj.y;
15666
15667         var el = this.getPositionEl();
15668         if(ax !== undefined || ay !== undefined){
15669             if(ax !== undefined && ay !== undefined){
15670                 el.setLeftTop(ax, ay);
15671             }else if(ax !== undefined){
15672                 el.setLeft(ax);
15673             }else if(ay !== undefined){
15674                 el.setTop(ay);
15675             }
15676             this.onPosition(ax, ay);
15677             this.fireEvent('move', this, ax, ay);
15678         }
15679         return this;
15680     },
15681
15682     /**
15683      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15684      * This method fires the move event.
15685      * @param {Number} x The new x position
15686      * @param {Number} y The new y position
15687      * @returns {Roo.BoxComponent} this
15688      */
15689     setPagePosition : function(x, y){
15690         this.pageX = x;
15691         this.pageY = y;
15692         if(!this.boxReady){
15693             return;
15694         }
15695         if(x === undefined || y === undefined){ // cannot translate undefined points
15696             return;
15697         }
15698         var p = this.el.translatePoints(x, y);
15699         this.setPosition(p.left, p.top);
15700         return this;
15701     },
15702
15703     // private
15704     onRender : function(ct, position){
15705         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15706         if(this.resizeEl){
15707             this.resizeEl = Roo.get(this.resizeEl);
15708         }
15709         if(this.positionEl){
15710             this.positionEl = Roo.get(this.positionEl);
15711         }
15712     },
15713
15714     // private
15715     afterRender : function(){
15716         Roo.BoxComponent.superclass.afterRender.call(this);
15717         this.boxReady = true;
15718         this.setSize(this.width, this.height);
15719         if(this.x || this.y){
15720             this.setPosition(this.x, this.y);
15721         }
15722         if(this.pageX || this.pageY){
15723             this.setPagePosition(this.pageX, this.pageY);
15724         }
15725     },
15726
15727     /**
15728      * Force the component's size to recalculate based on the underlying element's current height and width.
15729      * @returns {Roo.BoxComponent} this
15730      */
15731     syncSize : function(){
15732         delete this.lastSize;
15733         this.setSize(this.el.getWidth(), this.el.getHeight());
15734         return this;
15735     },
15736
15737     /**
15738      * Called after the component is resized, this method is empty by default but can be implemented by any
15739      * subclass that needs to perform custom logic after a resize occurs.
15740      * @param {Number} adjWidth The box-adjusted width that was set
15741      * @param {Number} adjHeight The box-adjusted height that was set
15742      * @param {Number} rawWidth The width that was originally specified
15743      * @param {Number} rawHeight The height that was originally specified
15744      */
15745     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15746
15747     },
15748
15749     /**
15750      * Called after the component is moved, this method is empty by default but can be implemented by any
15751      * subclass that needs to perform custom logic after a move occurs.
15752      * @param {Number} x The new x position
15753      * @param {Number} y The new y position
15754      */
15755     onPosition : function(x, y){
15756
15757     },
15758
15759     // private
15760     adjustSize : function(w, h){
15761         if(this.autoWidth){
15762             w = 'auto';
15763         }
15764         if(this.autoHeight){
15765             h = 'auto';
15766         }
15767         return {width : w, height: h};
15768     },
15769
15770     // private
15771     adjustPosition : function(x, y){
15772         return {x : x, y: y};
15773     }
15774 });/*
15775  * Original code for Roojs - LGPL
15776  * <script type="text/javascript">
15777  */
15778  
15779 /**
15780  * @class Roo.XComponent
15781  * A delayed Element creator...
15782  * Or a way to group chunks of interface together.
15783  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15784  *  used in conjunction with XComponent.build() it will create an instance of each element,
15785  *  then call addxtype() to build the User interface.
15786  * 
15787  * Mypart.xyx = new Roo.XComponent({
15788
15789     parent : 'Mypart.xyz', // empty == document.element.!!
15790     order : '001',
15791     name : 'xxxx'
15792     region : 'xxxx'
15793     disabled : function() {} 
15794      
15795     tree : function() { // return an tree of xtype declared components
15796         var MODULE = this;
15797         return 
15798         {
15799             xtype : 'NestedLayoutPanel',
15800             // technicall
15801         }
15802      ]
15803  *})
15804  *
15805  *
15806  * It can be used to build a big heiracy, with parent etc.
15807  * or you can just use this to render a single compoent to a dom element
15808  * MYPART.render(Roo.Element | String(id) | dom_element )
15809  *
15810  *
15811  * Usage patterns.
15812  *
15813  * Classic Roo
15814  *
15815  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15816  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15817  *
15818  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15819  *
15820  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15821  * - if mulitple topModules exist, the last one is defined as the top module.
15822  *
15823  * Embeded Roo
15824  * 
15825  * When the top level or multiple modules are to embedded into a existing HTML page,
15826  * the parent element can container '#id' of the element where the module will be drawn.
15827  *
15828  * Bootstrap Roo
15829  *
15830  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15831  * it relies more on a include mechanism, where sub modules are included into an outer page.
15832  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15833  * 
15834  * Bootstrap Roo Included elements
15835  *
15836  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15837  * hence confusing the component builder as it thinks there are multiple top level elements. 
15838  *
15839  * 
15840  * 
15841  * @extends Roo.util.Observable
15842  * @constructor
15843  * @param cfg {Object} configuration of component
15844  * 
15845  */
15846 Roo.XComponent = function(cfg) {
15847     Roo.apply(this, cfg);
15848     this.addEvents({ 
15849         /**
15850              * @event built
15851              * Fires when this the componnt is built
15852              * @param {Roo.XComponent} c the component
15853              */
15854         'built' : true
15855         
15856     });
15857     this.region = this.region || 'center'; // default..
15858     Roo.XComponent.register(this);
15859     this.modules = false;
15860     this.el = false; // where the layout goes..
15861     
15862     
15863 }
15864 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15865     /**
15866      * @property el
15867      * The created element (with Roo.factory())
15868      * @type {Roo.Layout}
15869      */
15870     el  : false,
15871     
15872     /**
15873      * @property el
15874      * for BC  - use el in new code
15875      * @type {Roo.Layout}
15876      */
15877     panel : false,
15878     
15879     /**
15880      * @property layout
15881      * for BC  - use el in new code
15882      * @type {Roo.Layout}
15883      */
15884     layout : false,
15885     
15886      /**
15887      * @cfg {Function|boolean} disabled
15888      * If this module is disabled by some rule, return true from the funtion
15889      */
15890     disabled : false,
15891     
15892     /**
15893      * @cfg {String} parent 
15894      * Name of parent element which it get xtype added to..
15895      */
15896     parent: false,
15897     
15898     /**
15899      * @cfg {String} order
15900      * Used to set the order in which elements are created (usefull for multiple tabs)
15901      */
15902     
15903     order : false,
15904     /**
15905      * @cfg {String} name
15906      * String to display while loading.
15907      */
15908     name : false,
15909     /**
15910      * @cfg {String} region
15911      * Region to render component to (defaults to center)
15912      */
15913     region : 'center',
15914     
15915     /**
15916      * @cfg {Array} items
15917      * A single item array - the first element is the root of the tree..
15918      * It's done this way to stay compatible with the Xtype system...
15919      */
15920     items : false,
15921     
15922     /**
15923      * @property _tree
15924      * The method that retuns the tree of parts that make up this compoennt 
15925      * @type {function}
15926      */
15927     _tree  : false,
15928     
15929      /**
15930      * render
15931      * render element to dom or tree
15932      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15933      */
15934     
15935     render : function(el)
15936     {
15937         
15938         el = el || false;
15939         var hp = this.parent ? 1 : 0;
15940         Roo.debug &&  Roo.log(this);
15941         
15942         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15943             // if parent is a '#.....' string, then let's use that..
15944             var ename = this.parent.substr(1);
15945             this.parent = false;
15946             Roo.debug && Roo.log(ename);
15947             switch (ename) {
15948                 case 'bootstrap-body' :
15949                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15950                         this.parent = { el :  new  Roo.bootstrap.Body() };
15951                         Roo.debug && Roo.log("setting el to doc body");
15952                          
15953                     } else {
15954                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15955                     }
15956                     break;
15957                 case 'bootstrap':
15958                     this.parent = { el : true};
15959                     // fall through
15960                 default:
15961                     el = Roo.get(ename);
15962                     break;
15963             }
15964                 
15965             
15966             if (!el && !this.parent) {
15967                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15968                 return;
15969             }
15970         }
15971         Roo.debug && Roo.log("EL:");
15972         Roo.debug && Roo.log(el);
15973         Roo.debug && Roo.log("this.parent.el:");
15974         Roo.debug && Roo.log(this.parent.el);
15975         
15976         var tree = this._tree ? this._tree() : this.tree();
15977
15978         // altertive root elements ??? - we need a better way to indicate these.
15979         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15980                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15981         
15982         if (!this.parent && is_alt) {
15983             //el = Roo.get(document.body);
15984             this.parent = { el : true };
15985         }
15986             
15987             
15988         
15989         if (!this.parent) {
15990             
15991             Roo.debug && Roo.log("no parent - creating one");
15992             
15993             el = el ? Roo.get(el) : false;      
15994             
15995             // it's a top level one..
15996             this.parent =  {
15997                 el : new Roo.BorderLayout(el || document.body, {
15998                 
15999                      center: {
16000                          titlebar: false,
16001                          autoScroll:false,
16002                          closeOnTab: true,
16003                          tabPosition: 'top',
16004                           //resizeTabs: true,
16005                          alwaysShowTabs: el && hp? false :  true,
16006                          hideTabs: el || !hp ? true :  false,
16007                          minTabWidth: 140
16008                      }
16009                  })
16010             }
16011         }
16012         
16013         if (!this.parent.el) {
16014                 // probably an old style ctor, which has been disabled.
16015                 return;
16016
16017         }
16018                 // The 'tree' method is  '_tree now' 
16019             
16020         tree.region = tree.region || this.region;
16021         
16022         if (this.parent.el === true) {
16023             // bootstrap... - body..
16024             this.parent.el = Roo.factory(tree);
16025         }
16026         
16027         this.el = this.parent.el.addxtype(tree);
16028         this.fireEvent('built', this);
16029         
16030         this.panel = this.el;
16031         this.layout = this.panel.layout;
16032         this.parentLayout = this.parent.layout  || false;  
16033          
16034     }
16035     
16036 });
16037
16038 Roo.apply(Roo.XComponent, {
16039     /**
16040      * @property  hideProgress
16041      * true to disable the building progress bar.. usefull on single page renders.
16042      * @type Boolean
16043      */
16044     hideProgress : false,
16045     /**
16046      * @property  buildCompleted
16047      * True when the builder has completed building the interface.
16048      * @type Boolean
16049      */
16050     buildCompleted : false,
16051      
16052     /**
16053      * @property  topModule
16054      * the upper most module - uses document.element as it's constructor.
16055      * @type Object
16056      */
16057      
16058     topModule  : false,
16059       
16060     /**
16061      * @property  modules
16062      * array of modules to be created by registration system.
16063      * @type {Array} of Roo.XComponent
16064      */
16065     
16066     modules : [],
16067     /**
16068      * @property  elmodules
16069      * array of modules to be created by which use #ID 
16070      * @type {Array} of Roo.XComponent
16071      */
16072      
16073     elmodules : [],
16074
16075      /**
16076      * @property  build_from_html
16077      * Build elements from html - used by bootstrap HTML stuff 
16078      *    - this is cleared after build is completed
16079      * @type {boolean} true  (default false)
16080      */
16081      
16082     build_from_html : false,
16083
16084     /**
16085      * Register components to be built later.
16086      *
16087      * This solves the following issues
16088      * - Building is not done on page load, but after an authentication process has occured.
16089      * - Interface elements are registered on page load
16090      * - Parent Interface elements may not be loaded before child, so this handles that..
16091      * 
16092      *
16093      * example:
16094      * 
16095      * MyApp.register({
16096           order : '000001',
16097           module : 'Pman.Tab.projectMgr',
16098           region : 'center',
16099           parent : 'Pman.layout',
16100           disabled : false,  // or use a function..
16101         })
16102      
16103      * * @param {Object} details about module
16104      */
16105     register : function(obj) {
16106                 
16107         Roo.XComponent.event.fireEvent('register', obj);
16108         switch(typeof(obj.disabled) ) {
16109                 
16110             case 'undefined':
16111                 break;
16112             
16113             case 'function':
16114                 if ( obj.disabled() ) {
16115                         return;
16116                 }
16117                 break;
16118             
16119             default:
16120                 if (obj.disabled) {
16121                         return;
16122                 }
16123                 break;
16124         }
16125                 
16126         this.modules.push(obj);
16127          
16128     },
16129     /**
16130      * convert a string to an object..
16131      * eg. 'AAA.BBB' -> finds AAA.BBB
16132
16133      */
16134     
16135     toObject : function(str)
16136     {
16137         if (!str || typeof(str) == 'object') {
16138             return str;
16139         }
16140         if (str.substring(0,1) == '#') {
16141             return str;
16142         }
16143
16144         var ar = str.split('.');
16145         var rt, o;
16146         rt = ar.shift();
16147             /** eval:var:o */
16148         try {
16149             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16150         } catch (e) {
16151             throw "Module not found : " + str;
16152         }
16153         
16154         if (o === false) {
16155             throw "Module not found : " + str;
16156         }
16157         Roo.each(ar, function(e) {
16158             if (typeof(o[e]) == 'undefined') {
16159                 throw "Module not found : " + str;
16160             }
16161             o = o[e];
16162         });
16163         
16164         return o;
16165         
16166     },
16167     
16168     
16169     /**
16170      * move modules into their correct place in the tree..
16171      * 
16172      */
16173     preBuild : function ()
16174     {
16175         var _t = this;
16176         Roo.each(this.modules , function (obj)
16177         {
16178             Roo.XComponent.event.fireEvent('beforebuild', obj);
16179             
16180             var opar = obj.parent;
16181             try { 
16182                 obj.parent = this.toObject(opar);
16183             } catch(e) {
16184                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16185                 return;
16186             }
16187             
16188             if (!obj.parent) {
16189                 Roo.debug && Roo.log("GOT top level module");
16190                 Roo.debug && Roo.log(obj);
16191                 obj.modules = new Roo.util.MixedCollection(false, 
16192                     function(o) { return o.order + '' }
16193                 );
16194                 this.topModule = obj;
16195                 return;
16196             }
16197                         // parent is a string (usually a dom element name..)
16198             if (typeof(obj.parent) == 'string') {
16199                 this.elmodules.push(obj);
16200                 return;
16201             }
16202             if (obj.parent.constructor != Roo.XComponent) {
16203                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16204             }
16205             if (!obj.parent.modules) {
16206                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16207                     function(o) { return o.order + '' }
16208                 );
16209             }
16210             if (obj.parent.disabled) {
16211                 obj.disabled = true;
16212             }
16213             obj.parent.modules.add(obj);
16214         }, this);
16215     },
16216     
16217      /**
16218      * make a list of modules to build.
16219      * @return {Array} list of modules. 
16220      */ 
16221     
16222     buildOrder : function()
16223     {
16224         var _this = this;
16225         var cmp = function(a,b) {   
16226             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16227         };
16228         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16229             throw "No top level modules to build";
16230         }
16231         
16232         // make a flat list in order of modules to build.
16233         var mods = this.topModule ? [ this.topModule ] : [];
16234                 
16235         
16236         // elmodules (is a list of DOM based modules )
16237         Roo.each(this.elmodules, function(e) {
16238             mods.push(e);
16239             if (!this.topModule &&
16240                 typeof(e.parent) == 'string' &&
16241                 e.parent.substring(0,1) == '#' &&
16242                 Roo.get(e.parent.substr(1))
16243                ) {
16244                 
16245                 _this.topModule = e;
16246             }
16247             
16248         });
16249
16250         
16251         // add modules to their parents..
16252         var addMod = function(m) {
16253             Roo.debug && Roo.log("build Order: add: " + m.name);
16254                 
16255             mods.push(m);
16256             if (m.modules && !m.disabled) {
16257                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16258                 m.modules.keySort('ASC',  cmp );
16259                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16260     
16261                 m.modules.each(addMod);
16262             } else {
16263                 Roo.debug && Roo.log("build Order: no child modules");
16264             }
16265             // not sure if this is used any more..
16266             if (m.finalize) {
16267                 m.finalize.name = m.name + " (clean up) ";
16268                 mods.push(m.finalize);
16269             }
16270             
16271         }
16272         if (this.topModule && this.topModule.modules) { 
16273             this.topModule.modules.keySort('ASC',  cmp );
16274             this.topModule.modules.each(addMod);
16275         } 
16276         return mods;
16277     },
16278     
16279      /**
16280      * Build the registered modules.
16281      * @param {Object} parent element.
16282      * @param {Function} optional method to call after module has been added.
16283      * 
16284      */ 
16285    
16286     build : function(opts) 
16287     {
16288         
16289         if (typeof(opts) != 'undefined') {
16290             Roo.apply(this,opts);
16291         }
16292         
16293         this.preBuild();
16294         var mods = this.buildOrder();
16295       
16296         //this.allmods = mods;
16297         //Roo.debug && Roo.log(mods);
16298         //return;
16299         if (!mods.length) { // should not happen
16300             throw "NO modules!!!";
16301         }
16302         
16303         
16304         var msg = "Building Interface...";
16305         // flash it up as modal - so we store the mask!?
16306         if (!this.hideProgress && Roo.MessageBox) {
16307             Roo.MessageBox.show({ title: 'loading' });
16308             Roo.MessageBox.show({
16309                title: "Please wait...",
16310                msg: msg,
16311                width:450,
16312                progress:true,
16313                closable:false,
16314                modal: false
16315               
16316             });
16317         }
16318         var total = mods.length;
16319         
16320         var _this = this;
16321         var progressRun = function() {
16322             if (!mods.length) {
16323                 Roo.debug && Roo.log('hide?');
16324                 if (!this.hideProgress && Roo.MessageBox) {
16325                     Roo.MessageBox.hide();
16326                 }
16327                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16328                 
16329                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16330                 
16331                 // THE END...
16332                 return false;   
16333             }
16334             
16335             var m = mods.shift();
16336             
16337             
16338             Roo.debug && Roo.log(m);
16339             // not sure if this is supported any more.. - modules that are are just function
16340             if (typeof(m) == 'function') { 
16341                 m.call(this);
16342                 return progressRun.defer(10, _this);
16343             } 
16344             
16345             
16346             msg = "Building Interface " + (total  - mods.length) + 
16347                     " of " + total + 
16348                     (m.name ? (' - ' + m.name) : '');
16349                         Roo.debug && Roo.log(msg);
16350             if (!this.hideProgress &&  Roo.MessageBox) { 
16351                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16352             }
16353             
16354          
16355             // is the module disabled?
16356             var disabled = (typeof(m.disabled) == 'function') ?
16357                 m.disabled.call(m.module.disabled) : m.disabled;    
16358             
16359             
16360             if (disabled) {
16361                 return progressRun(); // we do not update the display!
16362             }
16363             
16364             // now build 
16365             
16366                         
16367                         
16368             m.render();
16369             // it's 10 on top level, and 1 on others??? why...
16370             return progressRun.defer(10, _this);
16371              
16372         }
16373         progressRun.defer(1, _this);
16374      
16375         
16376         
16377     },
16378         
16379         
16380         /**
16381          * Event Object.
16382          *
16383          *
16384          */
16385         event: false, 
16386     /**
16387          * wrapper for event.on - aliased later..  
16388          * Typically use to register a event handler for register:
16389          *
16390          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16391          *
16392          */
16393     on : false
16394    
16395     
16396     
16397 });
16398
16399 Roo.XComponent.event = new Roo.util.Observable({
16400                 events : { 
16401                         /**
16402                          * @event register
16403                          * Fires when an Component is registered,
16404                          * set the disable property on the Component to stop registration.
16405                          * @param {Roo.XComponent} c the component being registerd.
16406                          * 
16407                          */
16408                         'register' : true,
16409             /**
16410                          * @event beforebuild
16411                          * Fires before each Component is built
16412                          * can be used to apply permissions.
16413                          * @param {Roo.XComponent} c the component being registerd.
16414                          * 
16415                          */
16416                         'beforebuild' : true,
16417                         /**
16418                          * @event buildcomplete
16419                          * Fires on the top level element when all elements have been built
16420                          * @param {Roo.XComponent} the top level component.
16421                          */
16422                         'buildcomplete' : true
16423                         
16424                 }
16425 });
16426
16427 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16428  /*
16429  * Based on:
16430  * Ext JS Library 1.1.1
16431  * Copyright(c) 2006-2007, Ext JS, LLC.
16432  *
16433  * Originally Released Under LGPL - original licence link has changed is not relivant.
16434  *
16435  * Fork - LGPL
16436  * <script type="text/javascript">
16437  */
16438
16439
16440
16441 /*
16442  * These classes are derivatives of the similarly named classes in the YUI Library.
16443  * The original license:
16444  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16445  * Code licensed under the BSD License:
16446  * http://developer.yahoo.net/yui/license.txt
16447  */
16448
16449 (function() {
16450
16451 var Event=Roo.EventManager;
16452 var Dom=Roo.lib.Dom;
16453
16454 /**
16455  * @class Roo.dd.DragDrop
16456  * @extends Roo.util.Observable
16457  * Defines the interface and base operation of items that that can be
16458  * dragged or can be drop targets.  It was designed to be extended, overriding
16459  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16460  * Up to three html elements can be associated with a DragDrop instance:
16461  * <ul>
16462  * <li>linked element: the element that is passed into the constructor.
16463  * This is the element which defines the boundaries for interaction with
16464  * other DragDrop objects.</li>
16465  * <li>handle element(s): The drag operation only occurs if the element that
16466  * was clicked matches a handle element.  By default this is the linked
16467  * element, but there are times that you will want only a portion of the
16468  * linked element to initiate the drag operation, and the setHandleElId()
16469  * method provides a way to define this.</li>
16470  * <li>drag element: this represents the element that would be moved along
16471  * with the cursor during a drag operation.  By default, this is the linked
16472  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16473  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16474  * </li>
16475  * </ul>
16476  * This class should not be instantiated until the onload event to ensure that
16477  * the associated elements are available.
16478  * The following would define a DragDrop obj that would interact with any
16479  * other DragDrop obj in the "group1" group:
16480  * <pre>
16481  *  dd = new Roo.dd.DragDrop("div1", "group1");
16482  * </pre>
16483  * Since none of the event handlers have been implemented, nothing would
16484  * actually happen if you were to run the code above.  Normally you would
16485  * override this class or one of the default implementations, but you can
16486  * also override the methods you want on an instance of the class...
16487  * <pre>
16488  *  dd.onDragDrop = function(e, id) {
16489  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16490  *  }
16491  * </pre>
16492  * @constructor
16493  * @param {String} id of the element that is linked to this instance
16494  * @param {String} sGroup the group of related DragDrop objects
16495  * @param {object} config an object containing configurable attributes
16496  *                Valid properties for DragDrop:
16497  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16498  */
16499 Roo.dd.DragDrop = function(id, sGroup, config) {
16500     if (id) {
16501         this.init(id, sGroup, config);
16502     }
16503     
16504 };
16505
16506 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16507
16508     /**
16509      * The id of the element associated with this object.  This is what we
16510      * refer to as the "linked element" because the size and position of
16511      * this element is used to determine when the drag and drop objects have
16512      * interacted.
16513      * @property id
16514      * @type String
16515      */
16516     id: null,
16517
16518     /**
16519      * Configuration attributes passed into the constructor
16520      * @property config
16521      * @type object
16522      */
16523     config: null,
16524
16525     /**
16526      * The id of the element that will be dragged.  By default this is same
16527      * as the linked element , but could be changed to another element. Ex:
16528      * Roo.dd.DDProxy
16529      * @property dragElId
16530      * @type String
16531      * @private
16532      */
16533     dragElId: null,
16534
16535     /**
16536      * the id of the element that initiates the drag operation.  By default
16537      * this is the linked element, but could be changed to be a child of this
16538      * element.  This lets us do things like only starting the drag when the
16539      * header element within the linked html element is clicked.
16540      * @property handleElId
16541      * @type String
16542      * @private
16543      */
16544     handleElId: null,
16545
16546     /**
16547      * An associative array of HTML tags that will be ignored if clicked.
16548      * @property invalidHandleTypes
16549      * @type {string: string}
16550      */
16551     invalidHandleTypes: null,
16552
16553     /**
16554      * An associative array of ids for elements that will be ignored if clicked
16555      * @property invalidHandleIds
16556      * @type {string: string}
16557      */
16558     invalidHandleIds: null,
16559
16560     /**
16561      * An indexted array of css class names for elements that will be ignored
16562      * if clicked.
16563      * @property invalidHandleClasses
16564      * @type string[]
16565      */
16566     invalidHandleClasses: null,
16567
16568     /**
16569      * The linked element's absolute X position at the time the drag was
16570      * started
16571      * @property startPageX
16572      * @type int
16573      * @private
16574      */
16575     startPageX: 0,
16576
16577     /**
16578      * The linked element's absolute X position at the time the drag was
16579      * started
16580      * @property startPageY
16581      * @type int
16582      * @private
16583      */
16584     startPageY: 0,
16585
16586     /**
16587      * The group defines a logical collection of DragDrop objects that are
16588      * related.  Instances only get events when interacting with other
16589      * DragDrop object in the same group.  This lets us define multiple
16590      * groups using a single DragDrop subclass if we want.
16591      * @property groups
16592      * @type {string: string}
16593      */
16594     groups: null,
16595
16596     /**
16597      * Individual drag/drop instances can be locked.  This will prevent
16598      * onmousedown start drag.
16599      * @property locked
16600      * @type boolean
16601      * @private
16602      */
16603     locked: false,
16604
16605     /**
16606      * Lock this instance
16607      * @method lock
16608      */
16609     lock: function() { this.locked = true; },
16610
16611     /**
16612      * Unlock this instace
16613      * @method unlock
16614      */
16615     unlock: function() { this.locked = false; },
16616
16617     /**
16618      * By default, all insances can be a drop target.  This can be disabled by
16619      * setting isTarget to false.
16620      * @method isTarget
16621      * @type boolean
16622      */
16623     isTarget: true,
16624
16625     /**
16626      * The padding configured for this drag and drop object for calculating
16627      * the drop zone intersection with this object.
16628      * @method padding
16629      * @type int[]
16630      */
16631     padding: null,
16632
16633     /**
16634      * Cached reference to the linked element
16635      * @property _domRef
16636      * @private
16637      */
16638     _domRef: null,
16639
16640     /**
16641      * Internal typeof flag
16642      * @property __ygDragDrop
16643      * @private
16644      */
16645     __ygDragDrop: true,
16646
16647     /**
16648      * Set to true when horizontal contraints are applied
16649      * @property constrainX
16650      * @type boolean
16651      * @private
16652      */
16653     constrainX: false,
16654
16655     /**
16656      * Set to true when vertical contraints are applied
16657      * @property constrainY
16658      * @type boolean
16659      * @private
16660      */
16661     constrainY: false,
16662
16663     /**
16664      * The left constraint
16665      * @property minX
16666      * @type int
16667      * @private
16668      */
16669     minX: 0,
16670
16671     /**
16672      * The right constraint
16673      * @property maxX
16674      * @type int
16675      * @private
16676      */
16677     maxX: 0,
16678
16679     /**
16680      * The up constraint
16681      * @property minY
16682      * @type int
16683      * @type int
16684      * @private
16685      */
16686     minY: 0,
16687
16688     /**
16689      * The down constraint
16690      * @property maxY
16691      * @type int
16692      * @private
16693      */
16694     maxY: 0,
16695
16696     /**
16697      * Maintain offsets when we resetconstraints.  Set to true when you want
16698      * the position of the element relative to its parent to stay the same
16699      * when the page changes
16700      *
16701      * @property maintainOffset
16702      * @type boolean
16703      */
16704     maintainOffset: false,
16705
16706     /**
16707      * Array of pixel locations the element will snap to if we specified a
16708      * horizontal graduation/interval.  This array is generated automatically
16709      * when you define a tick interval.
16710      * @property xTicks
16711      * @type int[]
16712      */
16713     xTicks: null,
16714
16715     /**
16716      * Array of pixel locations the element will snap to if we specified a
16717      * vertical graduation/interval.  This array is generated automatically
16718      * when you define a tick interval.
16719      * @property yTicks
16720      * @type int[]
16721      */
16722     yTicks: null,
16723
16724     /**
16725      * By default the drag and drop instance will only respond to the primary
16726      * button click (left button for a right-handed mouse).  Set to true to
16727      * allow drag and drop to start with any mouse click that is propogated
16728      * by the browser
16729      * @property primaryButtonOnly
16730      * @type boolean
16731      */
16732     primaryButtonOnly: true,
16733
16734     /**
16735      * The availabe property is false until the linked dom element is accessible.
16736      * @property available
16737      * @type boolean
16738      */
16739     available: false,
16740
16741     /**
16742      * By default, drags can only be initiated if the mousedown occurs in the
16743      * region the linked element is.  This is done in part to work around a
16744      * bug in some browsers that mis-report the mousedown if the previous
16745      * mouseup happened outside of the window.  This property is set to true
16746      * if outer handles are defined.
16747      *
16748      * @property hasOuterHandles
16749      * @type boolean
16750      * @default false
16751      */
16752     hasOuterHandles: false,
16753
16754     /**
16755      * Code that executes immediately before the startDrag event
16756      * @method b4StartDrag
16757      * @private
16758      */
16759     b4StartDrag: function(x, y) { },
16760
16761     /**
16762      * Abstract method called after a drag/drop object is clicked
16763      * and the drag or mousedown time thresholds have beeen met.
16764      * @method startDrag
16765      * @param {int} X click location
16766      * @param {int} Y click location
16767      */
16768     startDrag: function(x, y) { /* override this */ },
16769
16770     /**
16771      * Code that executes immediately before the onDrag event
16772      * @method b4Drag
16773      * @private
16774      */
16775     b4Drag: function(e) { },
16776
16777     /**
16778      * Abstract method called during the onMouseMove event while dragging an
16779      * object.
16780      * @method onDrag
16781      * @param {Event} e the mousemove event
16782      */
16783     onDrag: function(e) { /* override this */ },
16784
16785     /**
16786      * Abstract method called when this element fist begins hovering over
16787      * another DragDrop obj
16788      * @method onDragEnter
16789      * @param {Event} e the mousemove event
16790      * @param {String|DragDrop[]} id In POINT mode, the element
16791      * id this is hovering over.  In INTERSECT mode, an array of one or more
16792      * dragdrop items being hovered over.
16793      */
16794     onDragEnter: function(e, id) { /* override this */ },
16795
16796     /**
16797      * Code that executes immediately before the onDragOver event
16798      * @method b4DragOver
16799      * @private
16800      */
16801     b4DragOver: function(e) { },
16802
16803     /**
16804      * Abstract method called when this element is hovering over another
16805      * DragDrop obj
16806      * @method onDragOver
16807      * @param {Event} e the mousemove event
16808      * @param {String|DragDrop[]} id In POINT mode, the element
16809      * id this is hovering over.  In INTERSECT mode, an array of dd items
16810      * being hovered over.
16811      */
16812     onDragOver: function(e, id) { /* override this */ },
16813
16814     /**
16815      * Code that executes immediately before the onDragOut event
16816      * @method b4DragOut
16817      * @private
16818      */
16819     b4DragOut: function(e) { },
16820
16821     /**
16822      * Abstract method called when we are no longer hovering over an element
16823      * @method onDragOut
16824      * @param {Event} e the mousemove event
16825      * @param {String|DragDrop[]} id In POINT mode, the element
16826      * id this was hovering over.  In INTERSECT mode, an array of dd items
16827      * that the mouse is no longer over.
16828      */
16829     onDragOut: function(e, id) { /* override this */ },
16830
16831     /**
16832      * Code that executes immediately before the onDragDrop event
16833      * @method b4DragDrop
16834      * @private
16835      */
16836     b4DragDrop: function(e) { },
16837
16838     /**
16839      * Abstract method called when this item is dropped on another DragDrop
16840      * obj
16841      * @method onDragDrop
16842      * @param {Event} e the mouseup event
16843      * @param {String|DragDrop[]} id In POINT mode, the element
16844      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16845      * was dropped on.
16846      */
16847     onDragDrop: function(e, id) { /* override this */ },
16848
16849     /**
16850      * Abstract method called when this item is dropped on an area with no
16851      * drop target
16852      * @method onInvalidDrop
16853      * @param {Event} e the mouseup event
16854      */
16855     onInvalidDrop: function(e) { /* override this */ },
16856
16857     /**
16858      * Code that executes immediately before the endDrag event
16859      * @method b4EndDrag
16860      * @private
16861      */
16862     b4EndDrag: function(e) { },
16863
16864     /**
16865      * Fired when we are done dragging the object
16866      * @method endDrag
16867      * @param {Event} e the mouseup event
16868      */
16869     endDrag: function(e) { /* override this */ },
16870
16871     /**
16872      * Code executed immediately before the onMouseDown event
16873      * @method b4MouseDown
16874      * @param {Event} e the mousedown event
16875      * @private
16876      */
16877     b4MouseDown: function(e) {  },
16878
16879     /**
16880      * Event handler that fires when a drag/drop obj gets a mousedown
16881      * @method onMouseDown
16882      * @param {Event} e the mousedown event
16883      */
16884     onMouseDown: function(e) { /* override this */ },
16885
16886     /**
16887      * Event handler that fires when a drag/drop obj gets a mouseup
16888      * @method onMouseUp
16889      * @param {Event} e the mouseup event
16890      */
16891     onMouseUp: function(e) { /* override this */ },
16892
16893     /**
16894      * Override the onAvailable method to do what is needed after the initial
16895      * position was determined.
16896      * @method onAvailable
16897      */
16898     onAvailable: function () {
16899     },
16900
16901     /*
16902      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16903      * @type Object
16904      */
16905     defaultPadding : {left:0, right:0, top:0, bottom:0},
16906
16907     /*
16908      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16909  *
16910  * Usage:
16911  <pre><code>
16912  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16913                 { dragElId: "existingProxyDiv" });
16914  dd.startDrag = function(){
16915      this.constrainTo("parent-id");
16916  };
16917  </code></pre>
16918  * Or you can initalize it using the {@link Roo.Element} object:
16919  <pre><code>
16920  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16921      startDrag : function(){
16922          this.constrainTo("parent-id");
16923      }
16924  });
16925  </code></pre>
16926      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16927      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16928      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16929      * an object containing the sides to pad. For example: {right:10, bottom:10}
16930      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16931      */
16932     constrainTo : function(constrainTo, pad, inContent){
16933         if(typeof pad == "number"){
16934             pad = {left: pad, right:pad, top:pad, bottom:pad};
16935         }
16936         pad = pad || this.defaultPadding;
16937         var b = Roo.get(this.getEl()).getBox();
16938         var ce = Roo.get(constrainTo);
16939         var s = ce.getScroll();
16940         var c, cd = ce.dom;
16941         if(cd == document.body){
16942             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16943         }else{
16944             xy = ce.getXY();
16945             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16946         }
16947
16948
16949         var topSpace = b.y - c.y;
16950         var leftSpace = b.x - c.x;
16951
16952         this.resetConstraints();
16953         this.setXConstraint(leftSpace - (pad.left||0), // left
16954                 c.width - leftSpace - b.width - (pad.right||0) //right
16955         );
16956         this.setYConstraint(topSpace - (pad.top||0), //top
16957                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16958         );
16959     },
16960
16961     /**
16962      * Returns a reference to the linked element
16963      * @method getEl
16964      * @return {HTMLElement} the html element
16965      */
16966     getEl: function() {
16967         if (!this._domRef) {
16968             this._domRef = Roo.getDom(this.id);
16969         }
16970
16971         return this._domRef;
16972     },
16973
16974     /**
16975      * Returns a reference to the actual element to drag.  By default this is
16976      * the same as the html element, but it can be assigned to another
16977      * element. An example of this can be found in Roo.dd.DDProxy
16978      * @method getDragEl
16979      * @return {HTMLElement} the html element
16980      */
16981     getDragEl: function() {
16982         return Roo.getDom(this.dragElId);
16983     },
16984
16985     /**
16986      * Sets up the DragDrop object.  Must be called in the constructor of any
16987      * Roo.dd.DragDrop subclass
16988      * @method init
16989      * @param id the id of the linked element
16990      * @param {String} sGroup the group of related items
16991      * @param {object} config configuration attributes
16992      */
16993     init: function(id, sGroup, config) {
16994         this.initTarget(id, sGroup, config);
16995         if (!Roo.isTouch) {
16996             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16997         }
16998         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16999         // Event.on(this.id, "selectstart", Event.preventDefault);
17000     },
17001
17002     /**
17003      * Initializes Targeting functionality only... the object does not
17004      * get a mousedown handler.
17005      * @method initTarget
17006      * @param id the id of the linked element
17007      * @param {String} sGroup the group of related items
17008      * @param {object} config configuration attributes
17009      */
17010     initTarget: function(id, sGroup, config) {
17011
17012         // configuration attributes
17013         this.config = config || {};
17014
17015         // create a local reference to the drag and drop manager
17016         this.DDM = Roo.dd.DDM;
17017         // initialize the groups array
17018         this.groups = {};
17019
17020         // assume that we have an element reference instead of an id if the
17021         // parameter is not a string
17022         if (typeof id !== "string") {
17023             id = Roo.id(id);
17024         }
17025
17026         // set the id
17027         this.id = id;
17028
17029         // add to an interaction group
17030         this.addToGroup((sGroup) ? sGroup : "default");
17031
17032         // We don't want to register this as the handle with the manager
17033         // so we just set the id rather than calling the setter.
17034         this.handleElId = id;
17035
17036         // the linked element is the element that gets dragged by default
17037         this.setDragElId(id);
17038
17039         // by default, clicked anchors will not start drag operations.
17040         this.invalidHandleTypes = { A: "A" };
17041         this.invalidHandleIds = {};
17042         this.invalidHandleClasses = [];
17043
17044         this.applyConfig();
17045
17046         this.handleOnAvailable();
17047     },
17048
17049     /**
17050      * Applies the configuration parameters that were passed into the constructor.
17051      * This is supposed to happen at each level through the inheritance chain.  So
17052      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17053      * DragDrop in order to get all of the parameters that are available in
17054      * each object.
17055      * @method applyConfig
17056      */
17057     applyConfig: function() {
17058
17059         // configurable properties:
17060         //    padding, isTarget, maintainOffset, primaryButtonOnly
17061         this.padding           = this.config.padding || [0, 0, 0, 0];
17062         this.isTarget          = (this.config.isTarget !== false);
17063         this.maintainOffset    = (this.config.maintainOffset);
17064         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17065
17066     },
17067
17068     /**
17069      * Executed when the linked element is available
17070      * @method handleOnAvailable
17071      * @private
17072      */
17073     handleOnAvailable: function() {
17074         this.available = true;
17075         this.resetConstraints();
17076         this.onAvailable();
17077     },
17078
17079      /**
17080      * Configures the padding for the target zone in px.  Effectively expands
17081      * (or reduces) the virtual object size for targeting calculations.
17082      * Supports css-style shorthand; if only one parameter is passed, all sides
17083      * will have that padding, and if only two are passed, the top and bottom
17084      * will have the first param, the left and right the second.
17085      * @method setPadding
17086      * @param {int} iTop    Top pad
17087      * @param {int} iRight  Right pad
17088      * @param {int} iBot    Bot pad
17089      * @param {int} iLeft   Left pad
17090      */
17091     setPadding: function(iTop, iRight, iBot, iLeft) {
17092         // this.padding = [iLeft, iRight, iTop, iBot];
17093         if (!iRight && 0 !== iRight) {
17094             this.padding = [iTop, iTop, iTop, iTop];
17095         } else if (!iBot && 0 !== iBot) {
17096             this.padding = [iTop, iRight, iTop, iRight];
17097         } else {
17098             this.padding = [iTop, iRight, iBot, iLeft];
17099         }
17100     },
17101
17102     /**
17103      * Stores the initial placement of the linked element.
17104      * @method setInitialPosition
17105      * @param {int} diffX   the X offset, default 0
17106      * @param {int} diffY   the Y offset, default 0
17107      */
17108     setInitPosition: function(diffX, diffY) {
17109         var el = this.getEl();
17110
17111         if (!this.DDM.verifyEl(el)) {
17112             return;
17113         }
17114
17115         var dx = diffX || 0;
17116         var dy = diffY || 0;
17117
17118         var p = Dom.getXY( el );
17119
17120         this.initPageX = p[0] - dx;
17121         this.initPageY = p[1] - dy;
17122
17123         this.lastPageX = p[0];
17124         this.lastPageY = p[1];
17125
17126
17127         this.setStartPosition(p);
17128     },
17129
17130     /**
17131      * Sets the start position of the element.  This is set when the obj
17132      * is initialized, the reset when a drag is started.
17133      * @method setStartPosition
17134      * @param pos current position (from previous lookup)
17135      * @private
17136      */
17137     setStartPosition: function(pos) {
17138         var p = pos || Dom.getXY( this.getEl() );
17139         this.deltaSetXY = null;
17140
17141         this.startPageX = p[0];
17142         this.startPageY = p[1];
17143     },
17144
17145     /**
17146      * Add this instance to a group of related drag/drop objects.  All
17147      * instances belong to at least one group, and can belong to as many
17148      * groups as needed.
17149      * @method addToGroup
17150      * @param sGroup {string} the name of the group
17151      */
17152     addToGroup: function(sGroup) {
17153         this.groups[sGroup] = true;
17154         this.DDM.regDragDrop(this, sGroup);
17155     },
17156
17157     /**
17158      * Remove's this instance from the supplied interaction group
17159      * @method removeFromGroup
17160      * @param {string}  sGroup  The group to drop
17161      */
17162     removeFromGroup: function(sGroup) {
17163         if (this.groups[sGroup]) {
17164             delete this.groups[sGroup];
17165         }
17166
17167         this.DDM.removeDDFromGroup(this, sGroup);
17168     },
17169
17170     /**
17171      * Allows you to specify that an element other than the linked element
17172      * will be moved with the cursor during a drag
17173      * @method setDragElId
17174      * @param id {string} the id of the element that will be used to initiate the drag
17175      */
17176     setDragElId: function(id) {
17177         this.dragElId = id;
17178     },
17179
17180     /**
17181      * Allows you to specify a child of the linked element that should be
17182      * used to initiate the drag operation.  An example of this would be if
17183      * you have a content div with text and links.  Clicking anywhere in the
17184      * content area would normally start the drag operation.  Use this method
17185      * to specify that an element inside of the content div is the element
17186      * that starts the drag operation.
17187      * @method setHandleElId
17188      * @param id {string} the id of the element that will be used to
17189      * initiate the drag.
17190      */
17191     setHandleElId: function(id) {
17192         if (typeof id !== "string") {
17193             id = Roo.id(id);
17194         }
17195         this.handleElId = id;
17196         this.DDM.regHandle(this.id, id);
17197     },
17198
17199     /**
17200      * Allows you to set an element outside of the linked element as a drag
17201      * handle
17202      * @method setOuterHandleElId
17203      * @param id the id of the element that will be used to initiate the drag
17204      */
17205     setOuterHandleElId: function(id) {
17206         if (typeof id !== "string") {
17207             id = Roo.id(id);
17208         }
17209         Event.on(id, "mousedown",
17210                 this.handleMouseDown, this);
17211         this.setHandleElId(id);
17212
17213         this.hasOuterHandles = true;
17214     },
17215
17216     /**
17217      * Remove all drag and drop hooks for this element
17218      * @method unreg
17219      */
17220     unreg: function() {
17221         Event.un(this.id, "mousedown",
17222                 this.handleMouseDown);
17223         Event.un(this.id, "touchstart",
17224                 this.handleMouseDown);
17225         this._domRef = null;
17226         this.DDM._remove(this);
17227     },
17228
17229     destroy : function(){
17230         this.unreg();
17231     },
17232
17233     /**
17234      * Returns true if this instance is locked, or the drag drop mgr is locked
17235      * (meaning that all drag/drop is disabled on the page.)
17236      * @method isLocked
17237      * @return {boolean} true if this obj or all drag/drop is locked, else
17238      * false
17239      */
17240     isLocked: function() {
17241         return (this.DDM.isLocked() || this.locked);
17242     },
17243
17244     /**
17245      * Fired when this object is clicked
17246      * @method handleMouseDown
17247      * @param {Event} e
17248      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17249      * @private
17250      */
17251     handleMouseDown: function(e, oDD){
17252      
17253         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17254             //Roo.log('not touch/ button !=0');
17255             return;
17256         }
17257         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17258             return; // double touch..
17259         }
17260         
17261
17262         if (this.isLocked()) {
17263             //Roo.log('locked');
17264             return;
17265         }
17266
17267         this.DDM.refreshCache(this.groups);
17268 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17269         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17270         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17271             //Roo.log('no outer handes or not over target');
17272                 // do nothing.
17273         } else {
17274 //            Roo.log('check validator');
17275             if (this.clickValidator(e)) {
17276 //                Roo.log('validate success');
17277                 // set the initial element position
17278                 this.setStartPosition();
17279
17280
17281                 this.b4MouseDown(e);
17282                 this.onMouseDown(e);
17283
17284                 this.DDM.handleMouseDown(e, this);
17285
17286                 this.DDM.stopEvent(e);
17287             } else {
17288
17289
17290             }
17291         }
17292     },
17293
17294     clickValidator: function(e) {
17295         var target = e.getTarget();
17296         return ( this.isValidHandleChild(target) &&
17297                     (this.id == this.handleElId ||
17298                         this.DDM.handleWasClicked(target, this.id)) );
17299     },
17300
17301     /**
17302      * Allows you to specify a tag name that should not start a drag operation
17303      * when clicked.  This is designed to facilitate embedding links within a
17304      * drag handle that do something other than start the drag.
17305      * @method addInvalidHandleType
17306      * @param {string} tagName the type of element to exclude
17307      */
17308     addInvalidHandleType: function(tagName) {
17309         var type = tagName.toUpperCase();
17310         this.invalidHandleTypes[type] = type;
17311     },
17312
17313     /**
17314      * Lets you to specify an element id for a child of a drag handle
17315      * that should not initiate a drag
17316      * @method addInvalidHandleId
17317      * @param {string} id the element id of the element you wish to ignore
17318      */
17319     addInvalidHandleId: function(id) {
17320         if (typeof id !== "string") {
17321             id = Roo.id(id);
17322         }
17323         this.invalidHandleIds[id] = id;
17324     },
17325
17326     /**
17327      * Lets you specify a css class of elements that will not initiate a drag
17328      * @method addInvalidHandleClass
17329      * @param {string} cssClass the class of the elements you wish to ignore
17330      */
17331     addInvalidHandleClass: function(cssClass) {
17332         this.invalidHandleClasses.push(cssClass);
17333     },
17334
17335     /**
17336      * Unsets an excluded tag name set by addInvalidHandleType
17337      * @method removeInvalidHandleType
17338      * @param {string} tagName the type of element to unexclude
17339      */
17340     removeInvalidHandleType: function(tagName) {
17341         var type = tagName.toUpperCase();
17342         // this.invalidHandleTypes[type] = null;
17343         delete this.invalidHandleTypes[type];
17344     },
17345
17346     /**
17347      * Unsets an invalid handle id
17348      * @method removeInvalidHandleId
17349      * @param {string} id the id of the element to re-enable
17350      */
17351     removeInvalidHandleId: function(id) {
17352         if (typeof id !== "string") {
17353             id = Roo.id(id);
17354         }
17355         delete this.invalidHandleIds[id];
17356     },
17357
17358     /**
17359      * Unsets an invalid css class
17360      * @method removeInvalidHandleClass
17361      * @param {string} cssClass the class of the element(s) you wish to
17362      * re-enable
17363      */
17364     removeInvalidHandleClass: function(cssClass) {
17365         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17366             if (this.invalidHandleClasses[i] == cssClass) {
17367                 delete this.invalidHandleClasses[i];
17368             }
17369         }
17370     },
17371
17372     /**
17373      * Checks the tag exclusion list to see if this click should be ignored
17374      * @method isValidHandleChild
17375      * @param {HTMLElement} node the HTMLElement to evaluate
17376      * @return {boolean} true if this is a valid tag type, false if not
17377      */
17378     isValidHandleChild: function(node) {
17379
17380         var valid = true;
17381         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17382         var nodeName;
17383         try {
17384             nodeName = node.nodeName.toUpperCase();
17385         } catch(e) {
17386             nodeName = node.nodeName;
17387         }
17388         valid = valid && !this.invalidHandleTypes[nodeName];
17389         valid = valid && !this.invalidHandleIds[node.id];
17390
17391         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17392             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17393         }
17394
17395
17396         return valid;
17397
17398     },
17399
17400     /**
17401      * Create the array of horizontal tick marks if an interval was specified
17402      * in setXConstraint().
17403      * @method setXTicks
17404      * @private
17405      */
17406     setXTicks: function(iStartX, iTickSize) {
17407         this.xTicks = [];
17408         this.xTickSize = iTickSize;
17409
17410         var tickMap = {};
17411
17412         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17413             if (!tickMap[i]) {
17414                 this.xTicks[this.xTicks.length] = i;
17415                 tickMap[i] = true;
17416             }
17417         }
17418
17419         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17420             if (!tickMap[i]) {
17421                 this.xTicks[this.xTicks.length] = i;
17422                 tickMap[i] = true;
17423             }
17424         }
17425
17426         this.xTicks.sort(this.DDM.numericSort) ;
17427     },
17428
17429     /**
17430      * Create the array of vertical tick marks if an interval was specified in
17431      * setYConstraint().
17432      * @method setYTicks
17433      * @private
17434      */
17435     setYTicks: function(iStartY, iTickSize) {
17436         this.yTicks = [];
17437         this.yTickSize = iTickSize;
17438
17439         var tickMap = {};
17440
17441         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17442             if (!tickMap[i]) {
17443                 this.yTicks[this.yTicks.length] = i;
17444                 tickMap[i] = true;
17445             }
17446         }
17447
17448         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17449             if (!tickMap[i]) {
17450                 this.yTicks[this.yTicks.length] = i;
17451                 tickMap[i] = true;
17452             }
17453         }
17454
17455         this.yTicks.sort(this.DDM.numericSort) ;
17456     },
17457
17458     /**
17459      * By default, the element can be dragged any place on the screen.  Use
17460      * this method to limit the horizontal travel of the element.  Pass in
17461      * 0,0 for the parameters if you want to lock the drag to the y axis.
17462      * @method setXConstraint
17463      * @param {int} iLeft the number of pixels the element can move to the left
17464      * @param {int} iRight the number of pixels the element can move to the
17465      * right
17466      * @param {int} iTickSize optional parameter for specifying that the
17467      * element
17468      * should move iTickSize pixels at a time.
17469      */
17470     setXConstraint: function(iLeft, iRight, iTickSize) {
17471         this.leftConstraint = iLeft;
17472         this.rightConstraint = iRight;
17473
17474         this.minX = this.initPageX - iLeft;
17475         this.maxX = this.initPageX + iRight;
17476         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17477
17478         this.constrainX = true;
17479     },
17480
17481     /**
17482      * Clears any constraints applied to this instance.  Also clears ticks
17483      * since they can't exist independent of a constraint at this time.
17484      * @method clearConstraints
17485      */
17486     clearConstraints: function() {
17487         this.constrainX = false;
17488         this.constrainY = false;
17489         this.clearTicks();
17490     },
17491
17492     /**
17493      * Clears any tick interval defined for this instance
17494      * @method clearTicks
17495      */
17496     clearTicks: function() {
17497         this.xTicks = null;
17498         this.yTicks = null;
17499         this.xTickSize = 0;
17500         this.yTickSize = 0;
17501     },
17502
17503     /**
17504      * By default, the element can be dragged any place on the screen.  Set
17505      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17506      * parameters if you want to lock the drag to the x axis.
17507      * @method setYConstraint
17508      * @param {int} iUp the number of pixels the element can move up
17509      * @param {int} iDown the number of pixels the element can move down
17510      * @param {int} iTickSize optional parameter for specifying that the
17511      * element should move iTickSize pixels at a time.
17512      */
17513     setYConstraint: function(iUp, iDown, iTickSize) {
17514         this.topConstraint = iUp;
17515         this.bottomConstraint = iDown;
17516
17517         this.minY = this.initPageY - iUp;
17518         this.maxY = this.initPageY + iDown;
17519         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17520
17521         this.constrainY = true;
17522
17523     },
17524
17525     /**
17526      * resetConstraints must be called if you manually reposition a dd element.
17527      * @method resetConstraints
17528      * @param {boolean} maintainOffset
17529      */
17530     resetConstraints: function() {
17531
17532
17533         // Maintain offsets if necessary
17534         if (this.initPageX || this.initPageX === 0) {
17535             // figure out how much this thing has moved
17536             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17537             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17538
17539             this.setInitPosition(dx, dy);
17540
17541         // This is the first time we have detected the element's position
17542         } else {
17543             this.setInitPosition();
17544         }
17545
17546         if (this.constrainX) {
17547             this.setXConstraint( this.leftConstraint,
17548                                  this.rightConstraint,
17549                                  this.xTickSize        );
17550         }
17551
17552         if (this.constrainY) {
17553             this.setYConstraint( this.topConstraint,
17554                                  this.bottomConstraint,
17555                                  this.yTickSize         );
17556         }
17557     },
17558
17559     /**
17560      * Normally the drag element is moved pixel by pixel, but we can specify
17561      * that it move a number of pixels at a time.  This method resolves the
17562      * location when we have it set up like this.
17563      * @method getTick
17564      * @param {int} val where we want to place the object
17565      * @param {int[]} tickArray sorted array of valid points
17566      * @return {int} the closest tick
17567      * @private
17568      */
17569     getTick: function(val, tickArray) {
17570
17571         if (!tickArray) {
17572             // If tick interval is not defined, it is effectively 1 pixel,
17573             // so we return the value passed to us.
17574             return val;
17575         } else if (tickArray[0] >= val) {
17576             // The value is lower than the first tick, so we return the first
17577             // tick.
17578             return tickArray[0];
17579         } else {
17580             for (var i=0, len=tickArray.length; i<len; ++i) {
17581                 var next = i + 1;
17582                 if (tickArray[next] && tickArray[next] >= val) {
17583                     var diff1 = val - tickArray[i];
17584                     var diff2 = tickArray[next] - val;
17585                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17586                 }
17587             }
17588
17589             // The value is larger than the last tick, so we return the last
17590             // tick.
17591             return tickArray[tickArray.length - 1];
17592         }
17593     },
17594
17595     /**
17596      * toString method
17597      * @method toString
17598      * @return {string} string representation of the dd obj
17599      */
17600     toString: function() {
17601         return ("DragDrop " + this.id);
17602     }
17603
17604 });
17605
17606 })();
17607 /*
17608  * Based on:
17609  * Ext JS Library 1.1.1
17610  * Copyright(c) 2006-2007, Ext JS, LLC.
17611  *
17612  * Originally Released Under LGPL - original licence link has changed is not relivant.
17613  *
17614  * Fork - LGPL
17615  * <script type="text/javascript">
17616  */
17617
17618
17619 /**
17620  * The drag and drop utility provides a framework for building drag and drop
17621  * applications.  In addition to enabling drag and drop for specific elements,
17622  * the drag and drop elements are tracked by the manager class, and the
17623  * interactions between the various elements are tracked during the drag and
17624  * the implementing code is notified about these important moments.
17625  */
17626
17627 // Only load the library once.  Rewriting the manager class would orphan
17628 // existing drag and drop instances.
17629 if (!Roo.dd.DragDropMgr) {
17630
17631 /**
17632  * @class Roo.dd.DragDropMgr
17633  * DragDropMgr is a singleton that tracks the element interaction for
17634  * all DragDrop items in the window.  Generally, you will not call
17635  * this class directly, but it does have helper methods that could
17636  * be useful in your DragDrop implementations.
17637  * @singleton
17638  */
17639 Roo.dd.DragDropMgr = function() {
17640
17641     var Event = Roo.EventManager;
17642
17643     return {
17644
17645         /**
17646          * Two dimensional Array of registered DragDrop objects.  The first
17647          * dimension is the DragDrop item group, the second the DragDrop
17648          * object.
17649          * @property ids
17650          * @type {string: string}
17651          * @private
17652          * @static
17653          */
17654         ids: {},
17655
17656         /**
17657          * Array of element ids defined as drag handles.  Used to determine
17658          * if the element that generated the mousedown event is actually the
17659          * handle and not the html element itself.
17660          * @property handleIds
17661          * @type {string: string}
17662          * @private
17663          * @static
17664          */
17665         handleIds: {},
17666
17667         /**
17668          * the DragDrop object that is currently being dragged
17669          * @property dragCurrent
17670          * @type DragDrop
17671          * @private
17672          * @static
17673          **/
17674         dragCurrent: null,
17675
17676         /**
17677          * the DragDrop object(s) that are being hovered over
17678          * @property dragOvers
17679          * @type Array
17680          * @private
17681          * @static
17682          */
17683         dragOvers: {},
17684
17685         /**
17686          * the X distance between the cursor and the object being dragged
17687          * @property deltaX
17688          * @type int
17689          * @private
17690          * @static
17691          */
17692         deltaX: 0,
17693
17694         /**
17695          * the Y distance between the cursor and the object being dragged
17696          * @property deltaY
17697          * @type int
17698          * @private
17699          * @static
17700          */
17701         deltaY: 0,
17702
17703         /**
17704          * Flag to determine if we should prevent the default behavior of the
17705          * events we define. By default this is true, but this can be set to
17706          * false if you need the default behavior (not recommended)
17707          * @property preventDefault
17708          * @type boolean
17709          * @static
17710          */
17711         preventDefault: true,
17712
17713         /**
17714          * Flag to determine if we should stop the propagation of the events
17715          * we generate. This is true by default but you may want to set it to
17716          * false if the html element contains other features that require the
17717          * mouse click.
17718          * @property stopPropagation
17719          * @type boolean
17720          * @static
17721          */
17722         stopPropagation: true,
17723
17724         /**
17725          * Internal flag that is set to true when drag and drop has been
17726          * intialized
17727          * @property initialized
17728          * @private
17729          * @static
17730          */
17731         initalized: false,
17732
17733         /**
17734          * All drag and drop can be disabled.
17735          * @property locked
17736          * @private
17737          * @static
17738          */
17739         locked: false,
17740
17741         /**
17742          * Called the first time an element is registered.
17743          * @method init
17744          * @private
17745          * @static
17746          */
17747         init: function() {
17748             this.initialized = true;
17749         },
17750
17751         /**
17752          * In point mode, drag and drop interaction is defined by the
17753          * location of the cursor during the drag/drop
17754          * @property POINT
17755          * @type int
17756          * @static
17757          */
17758         POINT: 0,
17759
17760         /**
17761          * In intersect mode, drag and drop interactio nis defined by the
17762          * overlap of two or more drag and drop objects.
17763          * @property INTERSECT
17764          * @type int
17765          * @static
17766          */
17767         INTERSECT: 1,
17768
17769         /**
17770          * The current drag and drop mode.  Default: POINT
17771          * @property mode
17772          * @type int
17773          * @static
17774          */
17775         mode: 0,
17776
17777         /**
17778          * Runs method on all drag and drop objects
17779          * @method _execOnAll
17780          * @private
17781          * @static
17782          */
17783         _execOnAll: function(sMethod, args) {
17784             for (var i in this.ids) {
17785                 for (var j in this.ids[i]) {
17786                     var oDD = this.ids[i][j];
17787                     if (! this.isTypeOfDD(oDD)) {
17788                         continue;
17789                     }
17790                     oDD[sMethod].apply(oDD, args);
17791                 }
17792             }
17793         },
17794
17795         /**
17796          * Drag and drop initialization.  Sets up the global event handlers
17797          * @method _onLoad
17798          * @private
17799          * @static
17800          */
17801         _onLoad: function() {
17802
17803             this.init();
17804
17805             if (!Roo.isTouch) {
17806                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17807                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17808             }
17809             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17810             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17811             
17812             Event.on(window,   "unload",    this._onUnload, this, true);
17813             Event.on(window,   "resize",    this._onResize, this, true);
17814             // Event.on(window,   "mouseout",    this._test);
17815
17816         },
17817
17818         /**
17819          * Reset constraints on all drag and drop objs
17820          * @method _onResize
17821          * @private
17822          * @static
17823          */
17824         _onResize: function(e) {
17825             this._execOnAll("resetConstraints", []);
17826         },
17827
17828         /**
17829          * Lock all drag and drop functionality
17830          * @method lock
17831          * @static
17832          */
17833         lock: function() { this.locked = true; },
17834
17835         /**
17836          * Unlock all drag and drop functionality
17837          * @method unlock
17838          * @static
17839          */
17840         unlock: function() { this.locked = false; },
17841
17842         /**
17843          * Is drag and drop locked?
17844          * @method isLocked
17845          * @return {boolean} True if drag and drop is locked, false otherwise.
17846          * @static
17847          */
17848         isLocked: function() { return this.locked; },
17849
17850         /**
17851          * Location cache that is set for all drag drop objects when a drag is
17852          * initiated, cleared when the drag is finished.
17853          * @property locationCache
17854          * @private
17855          * @static
17856          */
17857         locationCache: {},
17858
17859         /**
17860          * Set useCache to false if you want to force object the lookup of each
17861          * drag and drop linked element constantly during a drag.
17862          * @property useCache
17863          * @type boolean
17864          * @static
17865          */
17866         useCache: true,
17867
17868         /**
17869          * The number of pixels that the mouse needs to move after the
17870          * mousedown before the drag is initiated.  Default=3;
17871          * @property clickPixelThresh
17872          * @type int
17873          * @static
17874          */
17875         clickPixelThresh: 3,
17876
17877         /**
17878          * The number of milliseconds after the mousedown event to initiate the
17879          * drag if we don't get a mouseup event. Default=1000
17880          * @property clickTimeThresh
17881          * @type int
17882          * @static
17883          */
17884         clickTimeThresh: 350,
17885
17886         /**
17887          * Flag that indicates that either the drag pixel threshold or the
17888          * mousdown time threshold has been met
17889          * @property dragThreshMet
17890          * @type boolean
17891          * @private
17892          * @static
17893          */
17894         dragThreshMet: false,
17895
17896         /**
17897          * Timeout used for the click time threshold
17898          * @property clickTimeout
17899          * @type Object
17900          * @private
17901          * @static
17902          */
17903         clickTimeout: null,
17904
17905         /**
17906          * The X position of the mousedown event stored for later use when a
17907          * drag threshold is met.
17908          * @property startX
17909          * @type int
17910          * @private
17911          * @static
17912          */
17913         startX: 0,
17914
17915         /**
17916          * The Y position of the mousedown event stored for later use when a
17917          * drag threshold is met.
17918          * @property startY
17919          * @type int
17920          * @private
17921          * @static
17922          */
17923         startY: 0,
17924
17925         /**
17926          * Each DragDrop instance must be registered with the DragDropMgr.
17927          * This is executed in DragDrop.init()
17928          * @method regDragDrop
17929          * @param {DragDrop} oDD the DragDrop object to register
17930          * @param {String} sGroup the name of the group this element belongs to
17931          * @static
17932          */
17933         regDragDrop: function(oDD, sGroup) {
17934             if (!this.initialized) { this.init(); }
17935
17936             if (!this.ids[sGroup]) {
17937                 this.ids[sGroup] = {};
17938             }
17939             this.ids[sGroup][oDD.id] = oDD;
17940         },
17941
17942         /**
17943          * Removes the supplied dd instance from the supplied group. Executed
17944          * by DragDrop.removeFromGroup, so don't call this function directly.
17945          * @method removeDDFromGroup
17946          * @private
17947          * @static
17948          */
17949         removeDDFromGroup: function(oDD, sGroup) {
17950             if (!this.ids[sGroup]) {
17951                 this.ids[sGroup] = {};
17952             }
17953
17954             var obj = this.ids[sGroup];
17955             if (obj && obj[oDD.id]) {
17956                 delete obj[oDD.id];
17957             }
17958         },
17959
17960         /**
17961          * Unregisters a drag and drop item.  This is executed in
17962          * DragDrop.unreg, use that method instead of calling this directly.
17963          * @method _remove
17964          * @private
17965          * @static
17966          */
17967         _remove: function(oDD) {
17968             for (var g in oDD.groups) {
17969                 if (g && this.ids[g][oDD.id]) {
17970                     delete this.ids[g][oDD.id];
17971                 }
17972             }
17973             delete this.handleIds[oDD.id];
17974         },
17975
17976         /**
17977          * Each DragDrop handle element must be registered.  This is done
17978          * automatically when executing DragDrop.setHandleElId()
17979          * @method regHandle
17980          * @param {String} sDDId the DragDrop id this element is a handle for
17981          * @param {String} sHandleId the id of the element that is the drag
17982          * handle
17983          * @static
17984          */
17985         regHandle: function(sDDId, sHandleId) {
17986             if (!this.handleIds[sDDId]) {
17987                 this.handleIds[sDDId] = {};
17988             }
17989             this.handleIds[sDDId][sHandleId] = sHandleId;
17990         },
17991
17992         /**
17993          * Utility function to determine if a given element has been
17994          * registered as a drag drop item.
17995          * @method isDragDrop
17996          * @param {String} id the element id to check
17997          * @return {boolean} true if this element is a DragDrop item,
17998          * false otherwise
17999          * @static
18000          */
18001         isDragDrop: function(id) {
18002             return ( this.getDDById(id) ) ? true : false;
18003         },
18004
18005         /**
18006          * Returns the drag and drop instances that are in all groups the
18007          * passed in instance belongs to.
18008          * @method getRelated
18009          * @param {DragDrop} p_oDD the obj to get related data for
18010          * @param {boolean} bTargetsOnly if true, only return targetable objs
18011          * @return {DragDrop[]} the related instances
18012          * @static
18013          */
18014         getRelated: function(p_oDD, bTargetsOnly) {
18015             var oDDs = [];
18016             for (var i in p_oDD.groups) {
18017                 for (j in this.ids[i]) {
18018                     var dd = this.ids[i][j];
18019                     if (! this.isTypeOfDD(dd)) {
18020                         continue;
18021                     }
18022                     if (!bTargetsOnly || dd.isTarget) {
18023                         oDDs[oDDs.length] = dd;
18024                     }
18025                 }
18026             }
18027
18028             return oDDs;
18029         },
18030
18031         /**
18032          * Returns true if the specified dd target is a legal target for
18033          * the specifice drag obj
18034          * @method isLegalTarget
18035          * @param {DragDrop} the drag obj
18036          * @param {DragDrop} the target
18037          * @return {boolean} true if the target is a legal target for the
18038          * dd obj
18039          * @static
18040          */
18041         isLegalTarget: function (oDD, oTargetDD) {
18042             var targets = this.getRelated(oDD, true);
18043             for (var i=0, len=targets.length;i<len;++i) {
18044                 if (targets[i].id == oTargetDD.id) {
18045                     return true;
18046                 }
18047             }
18048
18049             return false;
18050         },
18051
18052         /**
18053          * My goal is to be able to transparently determine if an object is
18054          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18055          * returns "object", oDD.constructor.toString() always returns
18056          * "DragDrop" and not the name of the subclass.  So for now it just
18057          * evaluates a well-known variable in DragDrop.
18058          * @method isTypeOfDD
18059          * @param {Object} the object to evaluate
18060          * @return {boolean} true if typeof oDD = DragDrop
18061          * @static
18062          */
18063         isTypeOfDD: function (oDD) {
18064             return (oDD && oDD.__ygDragDrop);
18065         },
18066
18067         /**
18068          * Utility function to determine if a given element has been
18069          * registered as a drag drop handle for the given Drag Drop object.
18070          * @method isHandle
18071          * @param {String} id the element id to check
18072          * @return {boolean} true if this element is a DragDrop handle, false
18073          * otherwise
18074          * @static
18075          */
18076         isHandle: function(sDDId, sHandleId) {
18077             return ( this.handleIds[sDDId] &&
18078                             this.handleIds[sDDId][sHandleId] );
18079         },
18080
18081         /**
18082          * Returns the DragDrop instance for a given id
18083          * @method getDDById
18084          * @param {String} id the id of the DragDrop object
18085          * @return {DragDrop} the drag drop object, null if it is not found
18086          * @static
18087          */
18088         getDDById: function(id) {
18089             for (var i in this.ids) {
18090                 if (this.ids[i][id]) {
18091                     return this.ids[i][id];
18092                 }
18093             }
18094             return null;
18095         },
18096
18097         /**
18098          * Fired after a registered DragDrop object gets the mousedown event.
18099          * Sets up the events required to track the object being dragged
18100          * @method handleMouseDown
18101          * @param {Event} e the event
18102          * @param oDD the DragDrop object being dragged
18103          * @private
18104          * @static
18105          */
18106         handleMouseDown: function(e, oDD) {
18107             if(Roo.QuickTips){
18108                 Roo.QuickTips.disable();
18109             }
18110             this.currentTarget = e.getTarget();
18111
18112             this.dragCurrent = oDD;
18113
18114             var el = oDD.getEl();
18115
18116             // track start position
18117             this.startX = e.getPageX();
18118             this.startY = e.getPageY();
18119
18120             this.deltaX = this.startX - el.offsetLeft;
18121             this.deltaY = this.startY - el.offsetTop;
18122
18123             this.dragThreshMet = false;
18124
18125             this.clickTimeout = setTimeout(
18126                     function() {
18127                         var DDM = Roo.dd.DDM;
18128                         DDM.startDrag(DDM.startX, DDM.startY);
18129                     },
18130                     this.clickTimeThresh );
18131         },
18132
18133         /**
18134          * Fired when either the drag pixel threshol or the mousedown hold
18135          * time threshold has been met.
18136          * @method startDrag
18137          * @param x {int} the X position of the original mousedown
18138          * @param y {int} the Y position of the original mousedown
18139          * @static
18140          */
18141         startDrag: function(x, y) {
18142             clearTimeout(this.clickTimeout);
18143             if (this.dragCurrent) {
18144                 this.dragCurrent.b4StartDrag(x, y);
18145                 this.dragCurrent.startDrag(x, y);
18146             }
18147             this.dragThreshMet = true;
18148         },
18149
18150         /**
18151          * Internal function to handle the mouseup event.  Will be invoked
18152          * from the context of the document.
18153          * @method handleMouseUp
18154          * @param {Event} e the event
18155          * @private
18156          * @static
18157          */
18158         handleMouseUp: function(e) {
18159
18160             if(Roo.QuickTips){
18161                 Roo.QuickTips.enable();
18162             }
18163             if (! this.dragCurrent) {
18164                 return;
18165             }
18166
18167             clearTimeout(this.clickTimeout);
18168
18169             if (this.dragThreshMet) {
18170                 this.fireEvents(e, true);
18171             } else {
18172             }
18173
18174             this.stopDrag(e);
18175
18176             this.stopEvent(e);
18177         },
18178
18179         /**
18180          * Utility to stop event propagation and event default, if these
18181          * features are turned on.
18182          * @method stopEvent
18183          * @param {Event} e the event as returned by this.getEvent()
18184          * @static
18185          */
18186         stopEvent: function(e){
18187             if(this.stopPropagation) {
18188                 e.stopPropagation();
18189             }
18190
18191             if (this.preventDefault) {
18192                 e.preventDefault();
18193             }
18194         },
18195
18196         /**
18197          * Internal function to clean up event handlers after the drag
18198          * operation is complete
18199          * @method stopDrag
18200          * @param {Event} e the event
18201          * @private
18202          * @static
18203          */
18204         stopDrag: function(e) {
18205             // Fire the drag end event for the item that was dragged
18206             if (this.dragCurrent) {
18207                 if (this.dragThreshMet) {
18208                     this.dragCurrent.b4EndDrag(e);
18209                     this.dragCurrent.endDrag(e);
18210                 }
18211
18212                 this.dragCurrent.onMouseUp(e);
18213             }
18214
18215             this.dragCurrent = null;
18216             this.dragOvers = {};
18217         },
18218
18219         /**
18220          * Internal function to handle the mousemove event.  Will be invoked
18221          * from the context of the html element.
18222          *
18223          * @TODO figure out what we can do about mouse events lost when the
18224          * user drags objects beyond the window boundary.  Currently we can
18225          * detect this in internet explorer by verifying that the mouse is
18226          * down during the mousemove event.  Firefox doesn't give us the
18227          * button state on the mousemove event.
18228          * @method handleMouseMove
18229          * @param {Event} e the event
18230          * @private
18231          * @static
18232          */
18233         handleMouseMove: function(e) {
18234             if (! this.dragCurrent) {
18235                 return true;
18236             }
18237
18238             // var button = e.which || e.button;
18239
18240             // check for IE mouseup outside of page boundary
18241             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18242                 this.stopEvent(e);
18243                 return this.handleMouseUp(e);
18244             }
18245
18246             if (!this.dragThreshMet) {
18247                 var diffX = Math.abs(this.startX - e.getPageX());
18248                 var diffY = Math.abs(this.startY - e.getPageY());
18249                 if (diffX > this.clickPixelThresh ||
18250                             diffY > this.clickPixelThresh) {
18251                     this.startDrag(this.startX, this.startY);
18252                 }
18253             }
18254
18255             if (this.dragThreshMet) {
18256                 this.dragCurrent.b4Drag(e);
18257                 this.dragCurrent.onDrag(e);
18258                 if(!this.dragCurrent.moveOnly){
18259                     this.fireEvents(e, false);
18260                 }
18261             }
18262
18263             this.stopEvent(e);
18264
18265             return true;
18266         },
18267
18268         /**
18269          * Iterates over all of the DragDrop elements to find ones we are
18270          * hovering over or dropping on
18271          * @method fireEvents
18272          * @param {Event} e the event
18273          * @param {boolean} isDrop is this a drop op or a mouseover op?
18274          * @private
18275          * @static
18276          */
18277         fireEvents: function(e, isDrop) {
18278             var dc = this.dragCurrent;
18279
18280             // If the user did the mouse up outside of the window, we could
18281             // get here even though we have ended the drag.
18282             if (!dc || dc.isLocked()) {
18283                 return;
18284             }
18285
18286             var pt = e.getPoint();
18287
18288             // cache the previous dragOver array
18289             var oldOvers = [];
18290
18291             var outEvts   = [];
18292             var overEvts  = [];
18293             var dropEvts  = [];
18294             var enterEvts = [];
18295
18296             // Check to see if the object(s) we were hovering over is no longer
18297             // being hovered over so we can fire the onDragOut event
18298             for (var i in this.dragOvers) {
18299
18300                 var ddo = this.dragOvers[i];
18301
18302                 if (! this.isTypeOfDD(ddo)) {
18303                     continue;
18304                 }
18305
18306                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18307                     outEvts.push( ddo );
18308                 }
18309
18310                 oldOvers[i] = true;
18311                 delete this.dragOvers[i];
18312             }
18313
18314             for (var sGroup in dc.groups) {
18315
18316                 if ("string" != typeof sGroup) {
18317                     continue;
18318                 }
18319
18320                 for (i in this.ids[sGroup]) {
18321                     var oDD = this.ids[sGroup][i];
18322                     if (! this.isTypeOfDD(oDD)) {
18323                         continue;
18324                     }
18325
18326                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18327                         if (this.isOverTarget(pt, oDD, this.mode)) {
18328                             // look for drop interactions
18329                             if (isDrop) {
18330                                 dropEvts.push( oDD );
18331                             // look for drag enter and drag over interactions
18332                             } else {
18333
18334                                 // initial drag over: dragEnter fires
18335                                 if (!oldOvers[oDD.id]) {
18336                                     enterEvts.push( oDD );
18337                                 // subsequent drag overs: dragOver fires
18338                                 } else {
18339                                     overEvts.push( oDD );
18340                                 }
18341
18342                                 this.dragOvers[oDD.id] = oDD;
18343                             }
18344                         }
18345                     }
18346                 }
18347             }
18348
18349             if (this.mode) {
18350                 if (outEvts.length) {
18351                     dc.b4DragOut(e, outEvts);
18352                     dc.onDragOut(e, outEvts);
18353                 }
18354
18355                 if (enterEvts.length) {
18356                     dc.onDragEnter(e, enterEvts);
18357                 }
18358
18359                 if (overEvts.length) {
18360                     dc.b4DragOver(e, overEvts);
18361                     dc.onDragOver(e, overEvts);
18362                 }
18363
18364                 if (dropEvts.length) {
18365                     dc.b4DragDrop(e, dropEvts);
18366                     dc.onDragDrop(e, dropEvts);
18367                 }
18368
18369             } else {
18370                 // fire dragout events
18371                 var len = 0;
18372                 for (i=0, len=outEvts.length; i<len; ++i) {
18373                     dc.b4DragOut(e, outEvts[i].id);
18374                     dc.onDragOut(e, outEvts[i].id);
18375                 }
18376
18377                 // fire enter events
18378                 for (i=0,len=enterEvts.length; i<len; ++i) {
18379                     // dc.b4DragEnter(e, oDD.id);
18380                     dc.onDragEnter(e, enterEvts[i].id);
18381                 }
18382
18383                 // fire over events
18384                 for (i=0,len=overEvts.length; i<len; ++i) {
18385                     dc.b4DragOver(e, overEvts[i].id);
18386                     dc.onDragOver(e, overEvts[i].id);
18387                 }
18388
18389                 // fire drop events
18390                 for (i=0, len=dropEvts.length; i<len; ++i) {
18391                     dc.b4DragDrop(e, dropEvts[i].id);
18392                     dc.onDragDrop(e, dropEvts[i].id);
18393                 }
18394
18395             }
18396
18397             // notify about a drop that did not find a target
18398             if (isDrop && !dropEvts.length) {
18399                 dc.onInvalidDrop(e);
18400             }
18401
18402         },
18403
18404         /**
18405          * Helper function for getting the best match from the list of drag
18406          * and drop objects returned by the drag and drop events when we are
18407          * in INTERSECT mode.  It returns either the first object that the
18408          * cursor is over, or the object that has the greatest overlap with
18409          * the dragged element.
18410          * @method getBestMatch
18411          * @param  {DragDrop[]} dds The array of drag and drop objects
18412          * targeted
18413          * @return {DragDrop}       The best single match
18414          * @static
18415          */
18416         getBestMatch: function(dds) {
18417             var winner = null;
18418             // Return null if the input is not what we expect
18419             //if (!dds || !dds.length || dds.length == 0) {
18420                // winner = null;
18421             // If there is only one item, it wins
18422             //} else if (dds.length == 1) {
18423
18424             var len = dds.length;
18425
18426             if (len == 1) {
18427                 winner = dds[0];
18428             } else {
18429                 // Loop through the targeted items
18430                 for (var i=0; i<len; ++i) {
18431                     var dd = dds[i];
18432                     // If the cursor is over the object, it wins.  If the
18433                     // cursor is over multiple matches, the first one we come
18434                     // to wins.
18435                     if (dd.cursorIsOver) {
18436                         winner = dd;
18437                         break;
18438                     // Otherwise the object with the most overlap wins
18439                     } else {
18440                         if (!winner ||
18441                             winner.overlap.getArea() < dd.overlap.getArea()) {
18442                             winner = dd;
18443                         }
18444                     }
18445                 }
18446             }
18447
18448             return winner;
18449         },
18450
18451         /**
18452          * Refreshes the cache of the top-left and bottom-right points of the
18453          * drag and drop objects in the specified group(s).  This is in the
18454          * format that is stored in the drag and drop instance, so typical
18455          * usage is:
18456          * <code>
18457          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18458          * </code>
18459          * Alternatively:
18460          * <code>
18461          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18462          * </code>
18463          * @TODO this really should be an indexed array.  Alternatively this
18464          * method could accept both.
18465          * @method refreshCache
18466          * @param {Object} groups an associative array of groups to refresh
18467          * @static
18468          */
18469         refreshCache: function(groups) {
18470             for (var sGroup in groups) {
18471                 if ("string" != typeof sGroup) {
18472                     continue;
18473                 }
18474                 for (var i in this.ids[sGroup]) {
18475                     var oDD = this.ids[sGroup][i];
18476
18477                     if (this.isTypeOfDD(oDD)) {
18478                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18479                         var loc = this.getLocation(oDD);
18480                         if (loc) {
18481                             this.locationCache[oDD.id] = loc;
18482                         } else {
18483                             delete this.locationCache[oDD.id];
18484                             // this will unregister the drag and drop object if
18485                             // the element is not in a usable state
18486                             // oDD.unreg();
18487                         }
18488                     }
18489                 }
18490             }
18491         },
18492
18493         /**
18494          * This checks to make sure an element exists and is in the DOM.  The
18495          * main purpose is to handle cases where innerHTML is used to remove
18496          * drag and drop objects from the DOM.  IE provides an 'unspecified
18497          * error' when trying to access the offsetParent of such an element
18498          * @method verifyEl
18499          * @param {HTMLElement} el the element to check
18500          * @return {boolean} true if the element looks usable
18501          * @static
18502          */
18503         verifyEl: function(el) {
18504             if (el) {
18505                 var parent;
18506                 if(Roo.isIE){
18507                     try{
18508                         parent = el.offsetParent;
18509                     }catch(e){}
18510                 }else{
18511                     parent = el.offsetParent;
18512                 }
18513                 if (parent) {
18514                     return true;
18515                 }
18516             }
18517
18518             return false;
18519         },
18520
18521         /**
18522          * Returns a Region object containing the drag and drop element's position
18523          * and size, including the padding configured for it
18524          * @method getLocation
18525          * @param {DragDrop} oDD the drag and drop object to get the
18526          *                       location for
18527          * @return {Roo.lib.Region} a Region object representing the total area
18528          *                             the element occupies, including any padding
18529          *                             the instance is configured for.
18530          * @static
18531          */
18532         getLocation: function(oDD) {
18533             if (! this.isTypeOfDD(oDD)) {
18534                 return null;
18535             }
18536
18537             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18538
18539             try {
18540                 pos= Roo.lib.Dom.getXY(el);
18541             } catch (e) { }
18542
18543             if (!pos) {
18544                 return null;
18545             }
18546
18547             x1 = pos[0];
18548             x2 = x1 + el.offsetWidth;
18549             y1 = pos[1];
18550             y2 = y1 + el.offsetHeight;
18551
18552             t = y1 - oDD.padding[0];
18553             r = x2 + oDD.padding[1];
18554             b = y2 + oDD.padding[2];
18555             l = x1 - oDD.padding[3];
18556
18557             return new Roo.lib.Region( t, r, b, l );
18558         },
18559
18560         /**
18561          * Checks the cursor location to see if it over the target
18562          * @method isOverTarget
18563          * @param {Roo.lib.Point} pt The point to evaluate
18564          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18565          * @return {boolean} true if the mouse is over the target
18566          * @private
18567          * @static
18568          */
18569         isOverTarget: function(pt, oTarget, intersect) {
18570             // use cache if available
18571             var loc = this.locationCache[oTarget.id];
18572             if (!loc || !this.useCache) {
18573                 loc = this.getLocation(oTarget);
18574                 this.locationCache[oTarget.id] = loc;
18575
18576             }
18577
18578             if (!loc) {
18579                 return false;
18580             }
18581
18582             oTarget.cursorIsOver = loc.contains( pt );
18583
18584             // DragDrop is using this as a sanity check for the initial mousedown
18585             // in this case we are done.  In POINT mode, if the drag obj has no
18586             // contraints, we are also done. Otherwise we need to evaluate the
18587             // location of the target as related to the actual location of the
18588             // dragged element.
18589             var dc = this.dragCurrent;
18590             if (!dc || !dc.getTargetCoord ||
18591                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18592                 return oTarget.cursorIsOver;
18593             }
18594
18595             oTarget.overlap = null;
18596
18597             // Get the current location of the drag element, this is the
18598             // location of the mouse event less the delta that represents
18599             // where the original mousedown happened on the element.  We
18600             // need to consider constraints and ticks as well.
18601             var pos = dc.getTargetCoord(pt.x, pt.y);
18602
18603             var el = dc.getDragEl();
18604             var curRegion = new Roo.lib.Region( pos.y,
18605                                                    pos.x + el.offsetWidth,
18606                                                    pos.y + el.offsetHeight,
18607                                                    pos.x );
18608
18609             var overlap = curRegion.intersect(loc);
18610
18611             if (overlap) {
18612                 oTarget.overlap = overlap;
18613                 return (intersect) ? true : oTarget.cursorIsOver;
18614             } else {
18615                 return false;
18616             }
18617         },
18618
18619         /**
18620          * unload event handler
18621          * @method _onUnload
18622          * @private
18623          * @static
18624          */
18625         _onUnload: function(e, me) {
18626             Roo.dd.DragDropMgr.unregAll();
18627         },
18628
18629         /**
18630          * Cleans up the drag and drop events and objects.
18631          * @method unregAll
18632          * @private
18633          * @static
18634          */
18635         unregAll: function() {
18636
18637             if (this.dragCurrent) {
18638                 this.stopDrag();
18639                 this.dragCurrent = null;
18640             }
18641
18642             this._execOnAll("unreg", []);
18643
18644             for (i in this.elementCache) {
18645                 delete this.elementCache[i];
18646             }
18647
18648             this.elementCache = {};
18649             this.ids = {};
18650         },
18651
18652         /**
18653          * A cache of DOM elements
18654          * @property elementCache
18655          * @private
18656          * @static
18657          */
18658         elementCache: {},
18659
18660         /**
18661          * Get the wrapper for the DOM element specified
18662          * @method getElWrapper
18663          * @param {String} id the id of the element to get
18664          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18665          * @private
18666          * @deprecated This wrapper isn't that useful
18667          * @static
18668          */
18669         getElWrapper: function(id) {
18670             var oWrapper = this.elementCache[id];
18671             if (!oWrapper || !oWrapper.el) {
18672                 oWrapper = this.elementCache[id] =
18673                     new this.ElementWrapper(Roo.getDom(id));
18674             }
18675             return oWrapper;
18676         },
18677
18678         /**
18679          * Returns the actual DOM element
18680          * @method getElement
18681          * @param {String} id the id of the elment to get
18682          * @return {Object} The element
18683          * @deprecated use Roo.getDom instead
18684          * @static
18685          */
18686         getElement: function(id) {
18687             return Roo.getDom(id);
18688         },
18689
18690         /**
18691          * Returns the style property for the DOM element (i.e.,
18692          * document.getElById(id).style)
18693          * @method getCss
18694          * @param {String} id the id of the elment to get
18695          * @return {Object} The style property of the element
18696          * @deprecated use Roo.getDom instead
18697          * @static
18698          */
18699         getCss: function(id) {
18700             var el = Roo.getDom(id);
18701             return (el) ? el.style : null;
18702         },
18703
18704         /**
18705          * Inner class for cached elements
18706          * @class DragDropMgr.ElementWrapper
18707          * @for DragDropMgr
18708          * @private
18709          * @deprecated
18710          */
18711         ElementWrapper: function(el) {
18712                 /**
18713                  * The element
18714                  * @property el
18715                  */
18716                 this.el = el || null;
18717                 /**
18718                  * The element id
18719                  * @property id
18720                  */
18721                 this.id = this.el && el.id;
18722                 /**
18723                  * A reference to the style property
18724                  * @property css
18725                  */
18726                 this.css = this.el && el.style;
18727             },
18728
18729         /**
18730          * Returns the X position of an html element
18731          * @method getPosX
18732          * @param el the element for which to get the position
18733          * @return {int} the X coordinate
18734          * @for DragDropMgr
18735          * @deprecated use Roo.lib.Dom.getX instead
18736          * @static
18737          */
18738         getPosX: function(el) {
18739             return Roo.lib.Dom.getX(el);
18740         },
18741
18742         /**
18743          * Returns the Y position of an html element
18744          * @method getPosY
18745          * @param el the element for which to get the position
18746          * @return {int} the Y coordinate
18747          * @deprecated use Roo.lib.Dom.getY instead
18748          * @static
18749          */
18750         getPosY: function(el) {
18751             return Roo.lib.Dom.getY(el);
18752         },
18753
18754         /**
18755          * Swap two nodes.  In IE, we use the native method, for others we
18756          * emulate the IE behavior
18757          * @method swapNode
18758          * @param n1 the first node to swap
18759          * @param n2 the other node to swap
18760          * @static
18761          */
18762         swapNode: function(n1, n2) {
18763             if (n1.swapNode) {
18764                 n1.swapNode(n2);
18765             } else {
18766                 var p = n2.parentNode;
18767                 var s = n2.nextSibling;
18768
18769                 if (s == n1) {
18770                     p.insertBefore(n1, n2);
18771                 } else if (n2 == n1.nextSibling) {
18772                     p.insertBefore(n2, n1);
18773                 } else {
18774                     n1.parentNode.replaceChild(n2, n1);
18775                     p.insertBefore(n1, s);
18776                 }
18777             }
18778         },
18779
18780         /**
18781          * Returns the current scroll position
18782          * @method getScroll
18783          * @private
18784          * @static
18785          */
18786         getScroll: function () {
18787             var t, l, dde=document.documentElement, db=document.body;
18788             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18789                 t = dde.scrollTop;
18790                 l = dde.scrollLeft;
18791             } else if (db) {
18792                 t = db.scrollTop;
18793                 l = db.scrollLeft;
18794             } else {
18795
18796             }
18797             return { top: t, left: l };
18798         },
18799
18800         /**
18801          * Returns the specified element style property
18802          * @method getStyle
18803          * @param {HTMLElement} el          the element
18804          * @param {string}      styleProp   the style property
18805          * @return {string} The value of the style property
18806          * @deprecated use Roo.lib.Dom.getStyle
18807          * @static
18808          */
18809         getStyle: function(el, styleProp) {
18810             return Roo.fly(el).getStyle(styleProp);
18811         },
18812
18813         /**
18814          * Gets the scrollTop
18815          * @method getScrollTop
18816          * @return {int} the document's scrollTop
18817          * @static
18818          */
18819         getScrollTop: function () { return this.getScroll().top; },
18820
18821         /**
18822          * Gets the scrollLeft
18823          * @method getScrollLeft
18824          * @return {int} the document's scrollTop
18825          * @static
18826          */
18827         getScrollLeft: function () { return this.getScroll().left; },
18828
18829         /**
18830          * Sets the x/y position of an element to the location of the
18831          * target element.
18832          * @method moveToEl
18833          * @param {HTMLElement} moveEl      The element to move
18834          * @param {HTMLElement} targetEl    The position reference element
18835          * @static
18836          */
18837         moveToEl: function (moveEl, targetEl) {
18838             var aCoord = Roo.lib.Dom.getXY(targetEl);
18839             Roo.lib.Dom.setXY(moveEl, aCoord);
18840         },
18841
18842         /**
18843          * Numeric array sort function
18844          * @method numericSort
18845          * @static
18846          */
18847         numericSort: function(a, b) { return (a - b); },
18848
18849         /**
18850          * Internal counter
18851          * @property _timeoutCount
18852          * @private
18853          * @static
18854          */
18855         _timeoutCount: 0,
18856
18857         /**
18858          * Trying to make the load order less important.  Without this we get
18859          * an error if this file is loaded before the Event Utility.
18860          * @method _addListeners
18861          * @private
18862          * @static
18863          */
18864         _addListeners: function() {
18865             var DDM = Roo.dd.DDM;
18866             if ( Roo.lib.Event && document ) {
18867                 DDM._onLoad();
18868             } else {
18869                 if (DDM._timeoutCount > 2000) {
18870                 } else {
18871                     setTimeout(DDM._addListeners, 10);
18872                     if (document && document.body) {
18873                         DDM._timeoutCount += 1;
18874                     }
18875                 }
18876             }
18877         },
18878
18879         /**
18880          * Recursively searches the immediate parent and all child nodes for
18881          * the handle element in order to determine wheter or not it was
18882          * clicked.
18883          * @method handleWasClicked
18884          * @param node the html element to inspect
18885          * @static
18886          */
18887         handleWasClicked: function(node, id) {
18888             if (this.isHandle(id, node.id)) {
18889                 return true;
18890             } else {
18891                 // check to see if this is a text node child of the one we want
18892                 var p = node.parentNode;
18893
18894                 while (p) {
18895                     if (this.isHandle(id, p.id)) {
18896                         return true;
18897                     } else {
18898                         p = p.parentNode;
18899                     }
18900                 }
18901             }
18902
18903             return false;
18904         }
18905
18906     };
18907
18908 }();
18909
18910 // shorter alias, save a few bytes
18911 Roo.dd.DDM = Roo.dd.DragDropMgr;
18912 Roo.dd.DDM._addListeners();
18913
18914 }/*
18915  * Based on:
18916  * Ext JS Library 1.1.1
18917  * Copyright(c) 2006-2007, Ext JS, LLC.
18918  *
18919  * Originally Released Under LGPL - original licence link has changed is not relivant.
18920  *
18921  * Fork - LGPL
18922  * <script type="text/javascript">
18923  */
18924
18925 /**
18926  * @class Roo.dd.DD
18927  * A DragDrop implementation where the linked element follows the
18928  * mouse cursor during a drag.
18929  * @extends Roo.dd.DragDrop
18930  * @constructor
18931  * @param {String} id the id of the linked element
18932  * @param {String} sGroup the group of related DragDrop items
18933  * @param {object} config an object containing configurable attributes
18934  *                Valid properties for DD:
18935  *                    scroll
18936  */
18937 Roo.dd.DD = function(id, sGroup, config) {
18938     if (id) {
18939         this.init(id, sGroup, config);
18940     }
18941 };
18942
18943 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18944
18945     /**
18946      * When set to true, the utility automatically tries to scroll the browser
18947      * window wehn a drag and drop element is dragged near the viewport boundary.
18948      * Defaults to true.
18949      * @property scroll
18950      * @type boolean
18951      */
18952     scroll: true,
18953
18954     /**
18955      * Sets the pointer offset to the distance between the linked element's top
18956      * left corner and the location the element was clicked
18957      * @method autoOffset
18958      * @param {int} iPageX the X coordinate of the click
18959      * @param {int} iPageY the Y coordinate of the click
18960      */
18961     autoOffset: function(iPageX, iPageY) {
18962         var x = iPageX - this.startPageX;
18963         var y = iPageY - this.startPageY;
18964         this.setDelta(x, y);
18965     },
18966
18967     /**
18968      * Sets the pointer offset.  You can call this directly to force the
18969      * offset to be in a particular location (e.g., pass in 0,0 to set it
18970      * to the center of the object)
18971      * @method setDelta
18972      * @param {int} iDeltaX the distance from the left
18973      * @param {int} iDeltaY the distance from the top
18974      */
18975     setDelta: function(iDeltaX, iDeltaY) {
18976         this.deltaX = iDeltaX;
18977         this.deltaY = iDeltaY;
18978     },
18979
18980     /**
18981      * Sets the drag element to the location of the mousedown or click event,
18982      * maintaining the cursor location relative to the location on the element
18983      * that was clicked.  Override this if you want to place the element in a
18984      * location other than where the cursor is.
18985      * @method setDragElPos
18986      * @param {int} iPageX the X coordinate of the mousedown or drag event
18987      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18988      */
18989     setDragElPos: function(iPageX, iPageY) {
18990         // the first time we do this, we are going to check to make sure
18991         // the element has css positioning
18992
18993         var el = this.getDragEl();
18994         this.alignElWithMouse(el, iPageX, iPageY);
18995     },
18996
18997     /**
18998      * Sets the element to the location of the mousedown or click event,
18999      * maintaining the cursor location relative to the location on the element
19000      * that was clicked.  Override this if you want to place the element in a
19001      * location other than where the cursor is.
19002      * @method alignElWithMouse
19003      * @param {HTMLElement} el the element to move
19004      * @param {int} iPageX the X coordinate of the mousedown or drag event
19005      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19006      */
19007     alignElWithMouse: function(el, iPageX, iPageY) {
19008         var oCoord = this.getTargetCoord(iPageX, iPageY);
19009         var fly = el.dom ? el : Roo.fly(el);
19010         if (!this.deltaSetXY) {
19011             var aCoord = [oCoord.x, oCoord.y];
19012             fly.setXY(aCoord);
19013             var newLeft = fly.getLeft(true);
19014             var newTop  = fly.getTop(true);
19015             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19016         } else {
19017             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19018         }
19019
19020         this.cachePosition(oCoord.x, oCoord.y);
19021         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19022         return oCoord;
19023     },
19024
19025     /**
19026      * Saves the most recent position so that we can reset the constraints and
19027      * tick marks on-demand.  We need to know this so that we can calculate the
19028      * number of pixels the element is offset from its original position.
19029      * @method cachePosition
19030      * @param iPageX the current x position (optional, this just makes it so we
19031      * don't have to look it up again)
19032      * @param iPageY the current y position (optional, this just makes it so we
19033      * don't have to look it up again)
19034      */
19035     cachePosition: function(iPageX, iPageY) {
19036         if (iPageX) {
19037             this.lastPageX = iPageX;
19038             this.lastPageY = iPageY;
19039         } else {
19040             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19041             this.lastPageX = aCoord[0];
19042             this.lastPageY = aCoord[1];
19043         }
19044     },
19045
19046     /**
19047      * Auto-scroll the window if the dragged object has been moved beyond the
19048      * visible window boundary.
19049      * @method autoScroll
19050      * @param {int} x the drag element's x position
19051      * @param {int} y the drag element's y position
19052      * @param {int} h the height of the drag element
19053      * @param {int} w the width of the drag element
19054      * @private
19055      */
19056     autoScroll: function(x, y, h, w) {
19057
19058         if (this.scroll) {
19059             // The client height
19060             var clientH = Roo.lib.Dom.getViewWidth();
19061
19062             // The client width
19063             var clientW = Roo.lib.Dom.getViewHeight();
19064
19065             // The amt scrolled down
19066             var st = this.DDM.getScrollTop();
19067
19068             // The amt scrolled right
19069             var sl = this.DDM.getScrollLeft();
19070
19071             // Location of the bottom of the element
19072             var bot = h + y;
19073
19074             // Location of the right of the element
19075             var right = w + x;
19076
19077             // The distance from the cursor to the bottom of the visible area,
19078             // adjusted so that we don't scroll if the cursor is beyond the
19079             // element drag constraints
19080             var toBot = (clientH + st - y - this.deltaY);
19081
19082             // The distance from the cursor to the right of the visible area
19083             var toRight = (clientW + sl - x - this.deltaX);
19084
19085
19086             // How close to the edge the cursor must be before we scroll
19087             // var thresh = (document.all) ? 100 : 40;
19088             var thresh = 40;
19089
19090             // How many pixels to scroll per autoscroll op.  This helps to reduce
19091             // clunky scrolling. IE is more sensitive about this ... it needs this
19092             // value to be higher.
19093             var scrAmt = (document.all) ? 80 : 30;
19094
19095             // Scroll down if we are near the bottom of the visible page and the
19096             // obj extends below the crease
19097             if ( bot > clientH && toBot < thresh ) {
19098                 window.scrollTo(sl, st + scrAmt);
19099             }
19100
19101             // Scroll up if the window is scrolled down and the top of the object
19102             // goes above the top border
19103             if ( y < st && st > 0 && y - st < thresh ) {
19104                 window.scrollTo(sl, st - scrAmt);
19105             }
19106
19107             // Scroll right if the obj is beyond the right border and the cursor is
19108             // near the border.
19109             if ( right > clientW && toRight < thresh ) {
19110                 window.scrollTo(sl + scrAmt, st);
19111             }
19112
19113             // Scroll left if the window has been scrolled to the right and the obj
19114             // extends past the left border
19115             if ( x < sl && sl > 0 && x - sl < thresh ) {
19116                 window.scrollTo(sl - scrAmt, st);
19117             }
19118         }
19119     },
19120
19121     /**
19122      * Finds the location the element should be placed if we want to move
19123      * it to where the mouse location less the click offset would place us.
19124      * @method getTargetCoord
19125      * @param {int} iPageX the X coordinate of the click
19126      * @param {int} iPageY the Y coordinate of the click
19127      * @return an object that contains the coordinates (Object.x and Object.y)
19128      * @private
19129      */
19130     getTargetCoord: function(iPageX, iPageY) {
19131
19132
19133         var x = iPageX - this.deltaX;
19134         var y = iPageY - this.deltaY;
19135
19136         if (this.constrainX) {
19137             if (x < this.minX) { x = this.minX; }
19138             if (x > this.maxX) { x = this.maxX; }
19139         }
19140
19141         if (this.constrainY) {
19142             if (y < this.minY) { y = this.minY; }
19143             if (y > this.maxY) { y = this.maxY; }
19144         }
19145
19146         x = this.getTick(x, this.xTicks);
19147         y = this.getTick(y, this.yTicks);
19148
19149
19150         return {x:x, y:y};
19151     },
19152
19153     /*
19154      * Sets up config options specific to this class. Overrides
19155      * Roo.dd.DragDrop, but all versions of this method through the
19156      * inheritance chain are called
19157      */
19158     applyConfig: function() {
19159         Roo.dd.DD.superclass.applyConfig.call(this);
19160         this.scroll = (this.config.scroll !== false);
19161     },
19162
19163     /*
19164      * Event that fires prior to the onMouseDown event.  Overrides
19165      * Roo.dd.DragDrop.
19166      */
19167     b4MouseDown: function(e) {
19168         // this.resetConstraints();
19169         this.autoOffset(e.getPageX(),
19170                             e.getPageY());
19171     },
19172
19173     /*
19174      * Event that fires prior to the onDrag event.  Overrides
19175      * Roo.dd.DragDrop.
19176      */
19177     b4Drag: function(e) {
19178         this.setDragElPos(e.getPageX(),
19179                             e.getPageY());
19180     },
19181
19182     toString: function() {
19183         return ("DD " + this.id);
19184     }
19185
19186     //////////////////////////////////////////////////////////////////////////
19187     // Debugging ygDragDrop events that can be overridden
19188     //////////////////////////////////////////////////////////////////////////
19189     /*
19190     startDrag: function(x, y) {
19191     },
19192
19193     onDrag: function(e) {
19194     },
19195
19196     onDragEnter: function(e, id) {
19197     },
19198
19199     onDragOver: function(e, id) {
19200     },
19201
19202     onDragOut: function(e, id) {
19203     },
19204
19205     onDragDrop: function(e, id) {
19206     },
19207
19208     endDrag: function(e) {
19209     }
19210
19211     */
19212
19213 });/*
19214  * Based on:
19215  * Ext JS Library 1.1.1
19216  * Copyright(c) 2006-2007, Ext JS, LLC.
19217  *
19218  * Originally Released Under LGPL - original licence link has changed is not relivant.
19219  *
19220  * Fork - LGPL
19221  * <script type="text/javascript">
19222  */
19223
19224 /**
19225  * @class Roo.dd.DDProxy
19226  * A DragDrop implementation that inserts an empty, bordered div into
19227  * the document that follows the cursor during drag operations.  At the time of
19228  * the click, the frame div is resized to the dimensions of the linked html
19229  * element, and moved to the exact location of the linked element.
19230  *
19231  * References to the "frame" element refer to the single proxy element that
19232  * was created to be dragged in place of all DDProxy elements on the
19233  * page.
19234  *
19235  * @extends Roo.dd.DD
19236  * @constructor
19237  * @param {String} id the id of the linked html element
19238  * @param {String} sGroup the group of related DragDrop objects
19239  * @param {object} config an object containing configurable attributes
19240  *                Valid properties for DDProxy in addition to those in DragDrop:
19241  *                   resizeFrame, centerFrame, dragElId
19242  */
19243 Roo.dd.DDProxy = function(id, sGroup, config) {
19244     if (id) {
19245         this.init(id, sGroup, config);
19246         this.initFrame();
19247     }
19248 };
19249
19250 /**
19251  * The default drag frame div id
19252  * @property Roo.dd.DDProxy.dragElId
19253  * @type String
19254  * @static
19255  */
19256 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19257
19258 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19259
19260     /**
19261      * By default we resize the drag frame to be the same size as the element
19262      * we want to drag (this is to get the frame effect).  We can turn it off
19263      * if we want a different behavior.
19264      * @property resizeFrame
19265      * @type boolean
19266      */
19267     resizeFrame: true,
19268
19269     /**
19270      * By default the frame is positioned exactly where the drag element is, so
19271      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19272      * you do not have constraints on the obj is to have the drag frame centered
19273      * around the cursor.  Set centerFrame to true for this effect.
19274      * @property centerFrame
19275      * @type boolean
19276      */
19277     centerFrame: false,
19278
19279     /**
19280      * Creates the proxy element if it does not yet exist
19281      * @method createFrame
19282      */
19283     createFrame: function() {
19284         var self = this;
19285         var body = document.body;
19286
19287         if (!body || !body.firstChild) {
19288             setTimeout( function() { self.createFrame(); }, 50 );
19289             return;
19290         }
19291
19292         var div = this.getDragEl();
19293
19294         if (!div) {
19295             div    = document.createElement("div");
19296             div.id = this.dragElId;
19297             var s  = div.style;
19298
19299             s.position   = "absolute";
19300             s.visibility = "hidden";
19301             s.cursor     = "move";
19302             s.border     = "2px solid #aaa";
19303             s.zIndex     = 999;
19304
19305             // appendChild can blow up IE if invoked prior to the window load event
19306             // while rendering a table.  It is possible there are other scenarios
19307             // that would cause this to happen as well.
19308             body.insertBefore(div, body.firstChild);
19309         }
19310     },
19311
19312     /**
19313      * Initialization for the drag frame element.  Must be called in the
19314      * constructor of all subclasses
19315      * @method initFrame
19316      */
19317     initFrame: function() {
19318         this.createFrame();
19319     },
19320
19321     applyConfig: function() {
19322         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19323
19324         this.resizeFrame = (this.config.resizeFrame !== false);
19325         this.centerFrame = (this.config.centerFrame);
19326         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19327     },
19328
19329     /**
19330      * Resizes the drag frame to the dimensions of the clicked object, positions
19331      * it over the object, and finally displays it
19332      * @method showFrame
19333      * @param {int} iPageX X click position
19334      * @param {int} iPageY Y click position
19335      * @private
19336      */
19337     showFrame: function(iPageX, iPageY) {
19338         var el = this.getEl();
19339         var dragEl = this.getDragEl();
19340         var s = dragEl.style;
19341
19342         this._resizeProxy();
19343
19344         if (this.centerFrame) {
19345             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19346                            Math.round(parseInt(s.height, 10)/2) );
19347         }
19348
19349         this.setDragElPos(iPageX, iPageY);
19350
19351         Roo.fly(dragEl).show();
19352     },
19353
19354     /**
19355      * The proxy is automatically resized to the dimensions of the linked
19356      * element when a drag is initiated, unless resizeFrame is set to false
19357      * @method _resizeProxy
19358      * @private
19359      */
19360     _resizeProxy: function() {
19361         if (this.resizeFrame) {
19362             var el = this.getEl();
19363             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19364         }
19365     },
19366
19367     // overrides Roo.dd.DragDrop
19368     b4MouseDown: function(e) {
19369         var x = e.getPageX();
19370         var y = e.getPageY();
19371         this.autoOffset(x, y);
19372         this.setDragElPos(x, y);
19373     },
19374
19375     // overrides Roo.dd.DragDrop
19376     b4StartDrag: function(x, y) {
19377         // show the drag frame
19378         this.showFrame(x, y);
19379     },
19380
19381     // overrides Roo.dd.DragDrop
19382     b4EndDrag: function(e) {
19383         Roo.fly(this.getDragEl()).hide();
19384     },
19385
19386     // overrides Roo.dd.DragDrop
19387     // By default we try to move the element to the last location of the frame.
19388     // This is so that the default behavior mirrors that of Roo.dd.DD.
19389     endDrag: function(e) {
19390
19391         var lel = this.getEl();
19392         var del = this.getDragEl();
19393
19394         // Show the drag frame briefly so we can get its position
19395         del.style.visibility = "";
19396
19397         this.beforeMove();
19398         // Hide the linked element before the move to get around a Safari
19399         // rendering bug.
19400         lel.style.visibility = "hidden";
19401         Roo.dd.DDM.moveToEl(lel, del);
19402         del.style.visibility = "hidden";
19403         lel.style.visibility = "";
19404
19405         this.afterDrag();
19406     },
19407
19408     beforeMove : function(){
19409
19410     },
19411
19412     afterDrag : function(){
19413
19414     },
19415
19416     toString: function() {
19417         return ("DDProxy " + this.id);
19418     }
19419
19420 });
19421 /*
19422  * Based on:
19423  * Ext JS Library 1.1.1
19424  * Copyright(c) 2006-2007, Ext JS, LLC.
19425  *
19426  * Originally Released Under LGPL - original licence link has changed is not relivant.
19427  *
19428  * Fork - LGPL
19429  * <script type="text/javascript">
19430  */
19431
19432  /**
19433  * @class Roo.dd.DDTarget
19434  * A DragDrop implementation that does not move, but can be a drop
19435  * target.  You would get the same result by simply omitting implementation
19436  * for the event callbacks, but this way we reduce the processing cost of the
19437  * event listener and the callbacks.
19438  * @extends Roo.dd.DragDrop
19439  * @constructor
19440  * @param {String} id the id of the element that is a drop target
19441  * @param {String} sGroup the group of related DragDrop objects
19442  * @param {object} config an object containing configurable attributes
19443  *                 Valid properties for DDTarget in addition to those in
19444  *                 DragDrop:
19445  *                    none
19446  */
19447 Roo.dd.DDTarget = function(id, sGroup, config) {
19448     if (id) {
19449         this.initTarget(id, sGroup, config);
19450     }
19451     if (config.listeners || config.events) { 
19452        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19453             listeners : config.listeners || {}, 
19454             events : config.events || {} 
19455         });    
19456     }
19457 };
19458
19459 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19460 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19461     toString: function() {
19462         return ("DDTarget " + this.id);
19463     }
19464 });
19465 /*
19466  * Based on:
19467  * Ext JS Library 1.1.1
19468  * Copyright(c) 2006-2007, Ext JS, LLC.
19469  *
19470  * Originally Released Under LGPL - original licence link has changed is not relivant.
19471  *
19472  * Fork - LGPL
19473  * <script type="text/javascript">
19474  */
19475  
19476
19477 /**
19478  * @class Roo.dd.ScrollManager
19479  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19480  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19481  * @singleton
19482  */
19483 Roo.dd.ScrollManager = function(){
19484     var ddm = Roo.dd.DragDropMgr;
19485     var els = {};
19486     var dragEl = null;
19487     var proc = {};
19488     
19489     
19490     
19491     var onStop = function(e){
19492         dragEl = null;
19493         clearProc();
19494     };
19495     
19496     var triggerRefresh = function(){
19497         if(ddm.dragCurrent){
19498              ddm.refreshCache(ddm.dragCurrent.groups);
19499         }
19500     };
19501     
19502     var doScroll = function(){
19503         if(ddm.dragCurrent){
19504             var dds = Roo.dd.ScrollManager;
19505             if(!dds.animate){
19506                 if(proc.el.scroll(proc.dir, dds.increment)){
19507                     triggerRefresh();
19508                 }
19509             }else{
19510                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19511             }
19512         }
19513     };
19514     
19515     var clearProc = function(){
19516         if(proc.id){
19517             clearInterval(proc.id);
19518         }
19519         proc.id = 0;
19520         proc.el = null;
19521         proc.dir = "";
19522     };
19523     
19524     var startProc = function(el, dir){
19525          Roo.log('scroll startproc');
19526         clearProc();
19527         proc.el = el;
19528         proc.dir = dir;
19529         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19530     };
19531     
19532     var onFire = function(e, isDrop){
19533        
19534         if(isDrop || !ddm.dragCurrent){ return; }
19535         var dds = Roo.dd.ScrollManager;
19536         if(!dragEl || dragEl != ddm.dragCurrent){
19537             dragEl = ddm.dragCurrent;
19538             // refresh regions on drag start
19539             dds.refreshCache();
19540         }
19541         
19542         var xy = Roo.lib.Event.getXY(e);
19543         var pt = new Roo.lib.Point(xy[0], xy[1]);
19544         for(var id in els){
19545             var el = els[id], r = el._region;
19546             if(r && r.contains(pt) && el.isScrollable()){
19547                 if(r.bottom - pt.y <= dds.thresh){
19548                     if(proc.el != el){
19549                         startProc(el, "down");
19550                     }
19551                     return;
19552                 }else if(r.right - pt.x <= dds.thresh){
19553                     if(proc.el != el){
19554                         startProc(el, "left");
19555                     }
19556                     return;
19557                 }else if(pt.y - r.top <= dds.thresh){
19558                     if(proc.el != el){
19559                         startProc(el, "up");
19560                     }
19561                     return;
19562                 }else if(pt.x - r.left <= dds.thresh){
19563                     if(proc.el != el){
19564                         startProc(el, "right");
19565                     }
19566                     return;
19567                 }
19568             }
19569         }
19570         clearProc();
19571     };
19572     
19573     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19574     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19575     
19576     return {
19577         /**
19578          * Registers new overflow element(s) to auto scroll
19579          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19580          */
19581         register : function(el){
19582             if(el instanceof Array){
19583                 for(var i = 0, len = el.length; i < len; i++) {
19584                         this.register(el[i]);
19585                 }
19586             }else{
19587                 el = Roo.get(el);
19588                 els[el.id] = el;
19589             }
19590             Roo.dd.ScrollManager.els = els;
19591         },
19592         
19593         /**
19594          * Unregisters overflow element(s) so they are no longer scrolled
19595          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19596          */
19597         unregister : function(el){
19598             if(el instanceof Array){
19599                 for(var i = 0, len = el.length; i < len; i++) {
19600                         this.unregister(el[i]);
19601                 }
19602             }else{
19603                 el = Roo.get(el);
19604                 delete els[el.id];
19605             }
19606         },
19607         
19608         /**
19609          * The number of pixels from the edge of a container the pointer needs to be to 
19610          * trigger scrolling (defaults to 25)
19611          * @type Number
19612          */
19613         thresh : 25,
19614         
19615         /**
19616          * The number of pixels to scroll in each scroll increment (defaults to 50)
19617          * @type Number
19618          */
19619         increment : 100,
19620         
19621         /**
19622          * The frequency of scrolls in milliseconds (defaults to 500)
19623          * @type Number
19624          */
19625         frequency : 500,
19626         
19627         /**
19628          * True to animate the scroll (defaults to true)
19629          * @type Boolean
19630          */
19631         animate: true,
19632         
19633         /**
19634          * The animation duration in seconds - 
19635          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19636          * @type Number
19637          */
19638         animDuration: .4,
19639         
19640         /**
19641          * Manually trigger a cache refresh.
19642          */
19643         refreshCache : function(){
19644             for(var id in els){
19645                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19646                     els[id]._region = els[id].getRegion();
19647                 }
19648             }
19649         }
19650     };
19651 }();/*
19652  * Based on:
19653  * Ext JS Library 1.1.1
19654  * Copyright(c) 2006-2007, Ext JS, LLC.
19655  *
19656  * Originally Released Under LGPL - original licence link has changed is not relivant.
19657  *
19658  * Fork - LGPL
19659  * <script type="text/javascript">
19660  */
19661  
19662
19663 /**
19664  * @class Roo.dd.Registry
19665  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19666  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19667  * @singleton
19668  */
19669 Roo.dd.Registry = function(){
19670     var elements = {}; 
19671     var handles = {}; 
19672     var autoIdSeed = 0;
19673
19674     var getId = function(el, autogen){
19675         if(typeof el == "string"){
19676             return el;
19677         }
19678         var id = el.id;
19679         if(!id && autogen !== false){
19680             id = "roodd-" + (++autoIdSeed);
19681             el.id = id;
19682         }
19683         return id;
19684     };
19685     
19686     return {
19687     /**
19688      * Register a drag drop element
19689      * @param {String|HTMLElement} element The id or DOM node to register
19690      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19691      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19692      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19693      * populated in the data object (if applicable):
19694      * <pre>
19695 Value      Description<br />
19696 ---------  ------------------------------------------<br />
19697 handles    Array of DOM nodes that trigger dragging<br />
19698            for the element being registered<br />
19699 isHandle   True if the element passed in triggers<br />
19700            dragging itself, else false
19701 </pre>
19702      */
19703         register : function(el, data){
19704             data = data || {};
19705             if(typeof el == "string"){
19706                 el = document.getElementById(el);
19707             }
19708             data.ddel = el;
19709             elements[getId(el)] = data;
19710             if(data.isHandle !== false){
19711                 handles[data.ddel.id] = data;
19712             }
19713             if(data.handles){
19714                 var hs = data.handles;
19715                 for(var i = 0, len = hs.length; i < len; i++){
19716                         handles[getId(hs[i])] = data;
19717                 }
19718             }
19719         },
19720
19721     /**
19722      * Unregister a drag drop element
19723      * @param {String|HTMLElement}  element The id or DOM node to unregister
19724      */
19725         unregister : function(el){
19726             var id = getId(el, false);
19727             var data = elements[id];
19728             if(data){
19729                 delete elements[id];
19730                 if(data.handles){
19731                     var hs = data.handles;
19732                     for(var i = 0, len = hs.length; i < len; i++){
19733                         delete handles[getId(hs[i], false)];
19734                     }
19735                 }
19736             }
19737         },
19738
19739     /**
19740      * Returns the handle registered for a DOM Node by id
19741      * @param {String|HTMLElement} id The DOM node or id to look up
19742      * @return {Object} handle The custom handle data
19743      */
19744         getHandle : function(id){
19745             if(typeof id != "string"){ // must be element?
19746                 id = id.id;
19747             }
19748             return handles[id];
19749         },
19750
19751     /**
19752      * Returns the handle that is registered for the DOM node that is the target of the event
19753      * @param {Event} e The event
19754      * @return {Object} handle The custom handle data
19755      */
19756         getHandleFromEvent : function(e){
19757             var t = Roo.lib.Event.getTarget(e);
19758             return t ? handles[t.id] : null;
19759         },
19760
19761     /**
19762      * Returns a custom data object that is registered for a DOM node by id
19763      * @param {String|HTMLElement} id The DOM node or id to look up
19764      * @return {Object} data The custom data
19765      */
19766         getTarget : function(id){
19767             if(typeof id != "string"){ // must be element?
19768                 id = id.id;
19769             }
19770             return elements[id];
19771         },
19772
19773     /**
19774      * Returns a custom data object that is registered for the DOM node that is the target of the event
19775      * @param {Event} e The event
19776      * @return {Object} data The custom data
19777      */
19778         getTargetFromEvent : function(e){
19779             var t = Roo.lib.Event.getTarget(e);
19780             return t ? elements[t.id] || handles[t.id] : null;
19781         }
19782     };
19783 }();/*
19784  * Based on:
19785  * Ext JS Library 1.1.1
19786  * Copyright(c) 2006-2007, Ext JS, LLC.
19787  *
19788  * Originally Released Under LGPL - original licence link has changed is not relivant.
19789  *
19790  * Fork - LGPL
19791  * <script type="text/javascript">
19792  */
19793  
19794
19795 /**
19796  * @class Roo.dd.StatusProxy
19797  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19798  * default drag proxy used by all Roo.dd components.
19799  * @constructor
19800  * @param {Object} config
19801  */
19802 Roo.dd.StatusProxy = function(config){
19803     Roo.apply(this, config);
19804     this.id = this.id || Roo.id();
19805     this.el = new Roo.Layer({
19806         dh: {
19807             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19808                 {tag: "div", cls: "x-dd-drop-icon"},
19809                 {tag: "div", cls: "x-dd-drag-ghost"}
19810             ]
19811         }, 
19812         shadow: !config || config.shadow !== false
19813     });
19814     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19815     this.dropStatus = this.dropNotAllowed;
19816 };
19817
19818 Roo.dd.StatusProxy.prototype = {
19819     /**
19820      * @cfg {String} dropAllowed
19821      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19822      */
19823     dropAllowed : "x-dd-drop-ok",
19824     /**
19825      * @cfg {String} dropNotAllowed
19826      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19827      */
19828     dropNotAllowed : "x-dd-drop-nodrop",
19829
19830     /**
19831      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19832      * over the current target element.
19833      * @param {String} cssClass The css class for the new drop status indicator image
19834      */
19835     setStatus : function(cssClass){
19836         cssClass = cssClass || this.dropNotAllowed;
19837         if(this.dropStatus != cssClass){
19838             this.el.replaceClass(this.dropStatus, cssClass);
19839             this.dropStatus = cssClass;
19840         }
19841     },
19842
19843     /**
19844      * Resets the status indicator to the default dropNotAllowed value
19845      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19846      */
19847     reset : function(clearGhost){
19848         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19849         this.dropStatus = this.dropNotAllowed;
19850         if(clearGhost){
19851             this.ghost.update("");
19852         }
19853     },
19854
19855     /**
19856      * Updates the contents of the ghost element
19857      * @param {String} html The html that will replace the current innerHTML of the ghost element
19858      */
19859     update : function(html){
19860         if(typeof html == "string"){
19861             this.ghost.update(html);
19862         }else{
19863             this.ghost.update("");
19864             html.style.margin = "0";
19865             this.ghost.dom.appendChild(html);
19866         }
19867         // ensure float = none set?? cant remember why though.
19868         var el = this.ghost.dom.firstChild;
19869                 if(el){
19870                         Roo.fly(el).setStyle('float', 'none');
19871                 }
19872     },
19873     
19874     /**
19875      * Returns the underlying proxy {@link Roo.Layer}
19876      * @return {Roo.Layer} el
19877     */
19878     getEl : function(){
19879         return this.el;
19880     },
19881
19882     /**
19883      * Returns the ghost element
19884      * @return {Roo.Element} el
19885      */
19886     getGhost : function(){
19887         return this.ghost;
19888     },
19889
19890     /**
19891      * Hides the proxy
19892      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19893      */
19894     hide : function(clear){
19895         this.el.hide();
19896         if(clear){
19897             this.reset(true);
19898         }
19899     },
19900
19901     /**
19902      * Stops the repair animation if it's currently running
19903      */
19904     stop : function(){
19905         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19906             this.anim.stop();
19907         }
19908     },
19909
19910     /**
19911      * Displays this proxy
19912      */
19913     show : function(){
19914         this.el.show();
19915     },
19916
19917     /**
19918      * Force the Layer to sync its shadow and shim positions to the element
19919      */
19920     sync : function(){
19921         this.el.sync();
19922     },
19923
19924     /**
19925      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19926      * invalid drop operation by the item being dragged.
19927      * @param {Array} xy The XY position of the element ([x, y])
19928      * @param {Function} callback The function to call after the repair is complete
19929      * @param {Object} scope The scope in which to execute the callback
19930      */
19931     repair : function(xy, callback, scope){
19932         this.callback = callback;
19933         this.scope = scope;
19934         if(xy && this.animRepair !== false){
19935             this.el.addClass("x-dd-drag-repair");
19936             this.el.hideUnders(true);
19937             this.anim = this.el.shift({
19938                 duration: this.repairDuration || .5,
19939                 easing: 'easeOut',
19940                 xy: xy,
19941                 stopFx: true,
19942                 callback: this.afterRepair,
19943                 scope: this
19944             });
19945         }else{
19946             this.afterRepair();
19947         }
19948     },
19949
19950     // private
19951     afterRepair : function(){
19952         this.hide(true);
19953         if(typeof this.callback == "function"){
19954             this.callback.call(this.scope || this);
19955         }
19956         this.callback = null;
19957         this.scope = null;
19958     }
19959 };/*
19960  * Based on:
19961  * Ext JS Library 1.1.1
19962  * Copyright(c) 2006-2007, Ext JS, LLC.
19963  *
19964  * Originally Released Under LGPL - original licence link has changed is not relivant.
19965  *
19966  * Fork - LGPL
19967  * <script type="text/javascript">
19968  */
19969
19970 /**
19971  * @class Roo.dd.DragSource
19972  * @extends Roo.dd.DDProxy
19973  * A simple class that provides the basic implementation needed to make any element draggable.
19974  * @constructor
19975  * @param {String/HTMLElement/Element} el The container element
19976  * @param {Object} config
19977  */
19978 Roo.dd.DragSource = function(el, config){
19979     this.el = Roo.get(el);
19980     this.dragData = {};
19981     
19982     Roo.apply(this, config);
19983     
19984     if(!this.proxy){
19985         this.proxy = new Roo.dd.StatusProxy();
19986     }
19987
19988     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19989           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19990     
19991     this.dragging = false;
19992 };
19993
19994 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19995     /**
19996      * @cfg {String} dropAllowed
19997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19998      */
19999     dropAllowed : "x-dd-drop-ok",
20000     /**
20001      * @cfg {String} dropNotAllowed
20002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20003      */
20004     dropNotAllowed : "x-dd-drop-nodrop",
20005
20006     /**
20007      * Returns the data object associated with this drag source
20008      * @return {Object} data An object containing arbitrary data
20009      */
20010     getDragData : function(e){
20011         return this.dragData;
20012     },
20013
20014     // private
20015     onDragEnter : function(e, id){
20016         var target = Roo.dd.DragDropMgr.getDDById(id);
20017         this.cachedTarget = target;
20018         if(this.beforeDragEnter(target, e, id) !== false){
20019             if(target.isNotifyTarget){
20020                 var status = target.notifyEnter(this, e, this.dragData);
20021                 this.proxy.setStatus(status);
20022             }else{
20023                 this.proxy.setStatus(this.dropAllowed);
20024             }
20025             
20026             if(this.afterDragEnter){
20027                 /**
20028                  * An empty function by default, but provided so that you can perform a custom action
20029                  * when the dragged item enters the drop target by providing an implementation.
20030                  * @param {Roo.dd.DragDrop} target The drop target
20031                  * @param {Event} e The event object
20032                  * @param {String} id The id of the dragged element
20033                  * @method afterDragEnter
20034                  */
20035                 this.afterDragEnter(target, e, id);
20036             }
20037         }
20038     },
20039
20040     /**
20041      * An empty function by default, but provided so that you can perform a custom action
20042      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20043      * @param {Roo.dd.DragDrop} target The drop target
20044      * @param {Event} e The event object
20045      * @param {String} id The id of the dragged element
20046      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20047      */
20048     beforeDragEnter : function(target, e, id){
20049         return true;
20050     },
20051
20052     // private
20053     alignElWithMouse: function() {
20054         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20055         this.proxy.sync();
20056     },
20057
20058     // private
20059     onDragOver : function(e, id){
20060         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20061         if(this.beforeDragOver(target, e, id) !== false){
20062             if(target.isNotifyTarget){
20063                 var status = target.notifyOver(this, e, this.dragData);
20064                 this.proxy.setStatus(status);
20065             }
20066
20067             if(this.afterDragOver){
20068                 /**
20069                  * An empty function by default, but provided so that you can perform a custom action
20070                  * while the dragged item is over the drop target by providing an implementation.
20071                  * @param {Roo.dd.DragDrop} target The drop target
20072                  * @param {Event} e The event object
20073                  * @param {String} id The id of the dragged element
20074                  * @method afterDragOver
20075                  */
20076                 this.afterDragOver(target, e, id);
20077             }
20078         }
20079     },
20080
20081     /**
20082      * An empty function by default, but provided so that you can perform a custom action
20083      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20084      * @param {Roo.dd.DragDrop} target The drop target
20085      * @param {Event} e The event object
20086      * @param {String} id The id of the dragged element
20087      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20088      */
20089     beforeDragOver : function(target, e, id){
20090         return true;
20091     },
20092
20093     // private
20094     onDragOut : function(e, id){
20095         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20096         if(this.beforeDragOut(target, e, id) !== false){
20097             if(target.isNotifyTarget){
20098                 target.notifyOut(this, e, this.dragData);
20099             }
20100             this.proxy.reset();
20101             if(this.afterDragOut){
20102                 /**
20103                  * An empty function by default, but provided so that you can perform a custom action
20104                  * after the dragged item is dragged out of the target without dropping.
20105                  * @param {Roo.dd.DragDrop} target The drop target
20106                  * @param {Event} e The event object
20107                  * @param {String} id The id of the dragged element
20108                  * @method afterDragOut
20109                  */
20110                 this.afterDragOut(target, e, id);
20111             }
20112         }
20113         this.cachedTarget = null;
20114     },
20115
20116     /**
20117      * An empty function by default, but provided so that you can perform a custom action before the dragged
20118      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20119      * @param {Roo.dd.DragDrop} target The drop target
20120      * @param {Event} e The event object
20121      * @param {String} id The id of the dragged element
20122      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20123      */
20124     beforeDragOut : function(target, e, id){
20125         return true;
20126     },
20127     
20128     // private
20129     onDragDrop : function(e, id){
20130         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20131         if(this.beforeDragDrop(target, e, id) !== false){
20132             if(target.isNotifyTarget){
20133                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20134                     this.onValidDrop(target, e, id);
20135                 }else{
20136                     this.onInvalidDrop(target, e, id);
20137                 }
20138             }else{
20139                 this.onValidDrop(target, e, id);
20140             }
20141             
20142             if(this.afterDragDrop){
20143                 /**
20144                  * An empty function by default, but provided so that you can perform a custom action
20145                  * after a valid drag drop has occurred by providing an implementation.
20146                  * @param {Roo.dd.DragDrop} target The drop target
20147                  * @param {Event} e The event object
20148                  * @param {String} id The id of the dropped element
20149                  * @method afterDragDrop
20150                  */
20151                 this.afterDragDrop(target, e, id);
20152             }
20153         }
20154         delete this.cachedTarget;
20155     },
20156
20157     /**
20158      * An empty function by default, but provided so that you can perform a custom action before the dragged
20159      * item is dropped onto the target and optionally cancel the onDragDrop.
20160      * @param {Roo.dd.DragDrop} target The drop target
20161      * @param {Event} e The event object
20162      * @param {String} id The id of the dragged element
20163      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20164      */
20165     beforeDragDrop : function(target, e, id){
20166         return true;
20167     },
20168
20169     // private
20170     onValidDrop : function(target, e, id){
20171         this.hideProxy();
20172         if(this.afterValidDrop){
20173             /**
20174              * An empty function by default, but provided so that you can perform a custom action
20175              * after a valid drop has occurred by providing an implementation.
20176              * @param {Object} target The target DD 
20177              * @param {Event} e The event object
20178              * @param {String} id The id of the dropped element
20179              * @method afterInvalidDrop
20180              */
20181             this.afterValidDrop(target, e, id);
20182         }
20183     },
20184
20185     // private
20186     getRepairXY : function(e, data){
20187         return this.el.getXY();  
20188     },
20189
20190     // private
20191     onInvalidDrop : function(target, e, id){
20192         this.beforeInvalidDrop(target, e, id);
20193         if(this.cachedTarget){
20194             if(this.cachedTarget.isNotifyTarget){
20195                 this.cachedTarget.notifyOut(this, e, this.dragData);
20196             }
20197             this.cacheTarget = null;
20198         }
20199         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20200
20201         if(this.afterInvalidDrop){
20202             /**
20203              * An empty function by default, but provided so that you can perform a custom action
20204              * after an invalid drop has occurred by providing an implementation.
20205              * @param {Event} e The event object
20206              * @param {String} id The id of the dropped element
20207              * @method afterInvalidDrop
20208              */
20209             this.afterInvalidDrop(e, id);
20210         }
20211     },
20212
20213     // private
20214     afterRepair : function(){
20215         if(Roo.enableFx){
20216             this.el.highlight(this.hlColor || "c3daf9");
20217         }
20218         this.dragging = false;
20219     },
20220
20221     /**
20222      * An empty function by default, but provided so that you can perform a custom action after an invalid
20223      * drop has occurred.
20224      * @param {Roo.dd.DragDrop} target The drop target
20225      * @param {Event} e The event object
20226      * @param {String} id The id of the dragged element
20227      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20228      */
20229     beforeInvalidDrop : function(target, e, id){
20230         return true;
20231     },
20232
20233     // private
20234     handleMouseDown : function(e){
20235         if(this.dragging) {
20236             return;
20237         }
20238         var data = this.getDragData(e);
20239         if(data && this.onBeforeDrag(data, e) !== false){
20240             this.dragData = data;
20241             this.proxy.stop();
20242             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20243         } 
20244     },
20245
20246     /**
20247      * An empty function by default, but provided so that you can perform a custom action before the initial
20248      * drag event begins and optionally cancel it.
20249      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20250      * @param {Event} e The event object
20251      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20252      */
20253     onBeforeDrag : function(data, e){
20254         return true;
20255     },
20256
20257     /**
20258      * An empty function by default, but provided so that you can perform a custom action once the initial
20259      * drag event has begun.  The drag cannot be canceled from this function.
20260      * @param {Number} x The x position of the click on the dragged object
20261      * @param {Number} y The y position of the click on the dragged object
20262      */
20263     onStartDrag : Roo.emptyFn,
20264
20265     // private - YUI override
20266     startDrag : function(x, y){
20267         this.proxy.reset();
20268         this.dragging = true;
20269         this.proxy.update("");
20270         this.onInitDrag(x, y);
20271         this.proxy.show();
20272     },
20273
20274     // private
20275     onInitDrag : function(x, y){
20276         var clone = this.el.dom.cloneNode(true);
20277         clone.id = Roo.id(); // prevent duplicate ids
20278         this.proxy.update(clone);
20279         this.onStartDrag(x, y);
20280         return true;
20281     },
20282
20283     /**
20284      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20285      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20286      */
20287     getProxy : function(){
20288         return this.proxy;  
20289     },
20290
20291     /**
20292      * Hides the drag source's {@link Roo.dd.StatusProxy}
20293      */
20294     hideProxy : function(){
20295         this.proxy.hide();  
20296         this.proxy.reset(true);
20297         this.dragging = false;
20298     },
20299
20300     // private
20301     triggerCacheRefresh : function(){
20302         Roo.dd.DDM.refreshCache(this.groups);
20303     },
20304
20305     // private - override to prevent hiding
20306     b4EndDrag: function(e) {
20307     },
20308
20309     // private - override to prevent moving
20310     endDrag : function(e){
20311         this.onEndDrag(this.dragData, e);
20312     },
20313
20314     // private
20315     onEndDrag : function(data, e){
20316     },
20317     
20318     // private - pin to cursor
20319     autoOffset : function(x, y) {
20320         this.setDelta(-12, -20);
20321     }    
20322 });/*
20323  * Based on:
20324  * Ext JS Library 1.1.1
20325  * Copyright(c) 2006-2007, Ext JS, LLC.
20326  *
20327  * Originally Released Under LGPL - original licence link has changed is not relivant.
20328  *
20329  * Fork - LGPL
20330  * <script type="text/javascript">
20331  */
20332
20333
20334 /**
20335  * @class Roo.dd.DropTarget
20336  * @extends Roo.dd.DDTarget
20337  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20338  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20339  * @constructor
20340  * @param {String/HTMLElement/Element} el The container element
20341  * @param {Object} config
20342  */
20343 Roo.dd.DropTarget = function(el, config){
20344     this.el = Roo.get(el);
20345     
20346     var listeners = false; ;
20347     if (config && config.listeners) {
20348         listeners= config.listeners;
20349         delete config.listeners;
20350     }
20351     Roo.apply(this, config);
20352     
20353     if(this.containerScroll){
20354         Roo.dd.ScrollManager.register(this.el);
20355     }
20356     this.addEvents( {
20357          /**
20358          * @scope Roo.dd.DropTarget
20359          */
20360          
20361          /**
20362          * @event enter
20363          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20364          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20365          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20366          * 
20367          * IMPORTANT : it should set this.overClass and this.dropAllowed
20368          * 
20369          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20370          * @param {Event} e The event
20371          * @param {Object} data An object containing arbitrary data supplied by the drag source
20372          */
20373         "enter" : true,
20374         
20375          /**
20376          * @event over
20377          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20378          * This method will be called on every mouse movement while the drag source is over the drop target.
20379          * This default implementation simply returns the dropAllowed config value.
20380          * 
20381          * IMPORTANT : it should set this.dropAllowed
20382          * 
20383          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20384          * @param {Event} e The event
20385          * @param {Object} data An object containing arbitrary data supplied by the drag source
20386          
20387          */
20388         "over" : true,
20389         /**
20390          * @event out
20391          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20392          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20393          * overClass (if any) from the drop element.
20394          * 
20395          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20396          * @param {Event} e The event
20397          * @param {Object} data An object containing arbitrary data supplied by the drag source
20398          */
20399          "out" : true,
20400          
20401         /**
20402          * @event drop
20403          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20404          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20405          * implementation that does something to process the drop event and returns true so that the drag source's
20406          * repair action does not run.
20407          * 
20408          * IMPORTANT : it should set this.success
20409          * 
20410          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20411          * @param {Event} e The event
20412          * @param {Object} data An object containing arbitrary data supplied by the drag source
20413         */
20414          "drop" : true
20415     });
20416             
20417      
20418     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20419         this.el.dom, 
20420         this.ddGroup || this.group,
20421         {
20422             isTarget: true,
20423             listeners : listeners || {} 
20424            
20425         
20426         }
20427     );
20428
20429 };
20430
20431 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20432     /**
20433      * @cfg {String} overClass
20434      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20435      */
20436      /**
20437      * @cfg {String} ddGroup
20438      * The drag drop group to handle drop events for
20439      */
20440      
20441     /**
20442      * @cfg {String} dropAllowed
20443      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20444      */
20445     dropAllowed : "x-dd-drop-ok",
20446     /**
20447      * @cfg {String} dropNotAllowed
20448      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20449      */
20450     dropNotAllowed : "x-dd-drop-nodrop",
20451     /**
20452      * @cfg {boolean} success
20453      * set this after drop listener.. 
20454      */
20455     success : false,
20456     /**
20457      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20458      * if the drop point is valid for over/enter..
20459      */
20460     valid : false,
20461     // private
20462     isTarget : true,
20463
20464     // private
20465     isNotifyTarget : true,
20466     
20467     /**
20468      * @hide
20469      */
20470     notifyEnter : function(dd, e, data)
20471     {
20472         this.valid = true;
20473         this.fireEvent('enter', dd, e, data);
20474         if(this.overClass){
20475             this.el.addClass(this.overClass);
20476         }
20477         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20478             this.valid ? this.dropAllowed : this.dropNotAllowed
20479         );
20480     },
20481
20482     /**
20483      * @hide
20484      */
20485     notifyOver : function(dd, e, data)
20486     {
20487         this.valid = true;
20488         this.fireEvent('over', dd, e, data);
20489         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20490             this.valid ? this.dropAllowed : this.dropNotAllowed
20491         );
20492     },
20493
20494     /**
20495      * @hide
20496      */
20497     notifyOut : function(dd, e, data)
20498     {
20499         this.fireEvent('out', dd, e, data);
20500         if(this.overClass){
20501             this.el.removeClass(this.overClass);
20502         }
20503     },
20504
20505     /**
20506      * @hide
20507      */
20508     notifyDrop : function(dd, e, data)
20509     {
20510         this.success = false;
20511         this.fireEvent('drop', dd, e, data);
20512         return this.success;
20513     }
20514 });/*
20515  * Based on:
20516  * Ext JS Library 1.1.1
20517  * Copyright(c) 2006-2007, Ext JS, LLC.
20518  *
20519  * Originally Released Under LGPL - original licence link has changed is not relivant.
20520  *
20521  * Fork - LGPL
20522  * <script type="text/javascript">
20523  */
20524
20525
20526 /**
20527  * @class Roo.dd.DragZone
20528  * @extends Roo.dd.DragSource
20529  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20530  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20531  * @constructor
20532  * @param {String/HTMLElement/Element} el The container element
20533  * @param {Object} config
20534  */
20535 Roo.dd.DragZone = function(el, config){
20536     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20537     if(this.containerScroll){
20538         Roo.dd.ScrollManager.register(this.el);
20539     }
20540 };
20541
20542 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20543     /**
20544      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20545      * for auto scrolling during drag operations.
20546      */
20547     /**
20548      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20549      * method after a failed drop (defaults to "c3daf9" - light blue)
20550      */
20551
20552     /**
20553      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20554      * for a valid target to drag based on the mouse down. Override this method
20555      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20556      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20557      * @param {EventObject} e The mouse down event
20558      * @return {Object} The dragData
20559      */
20560     getDragData : function(e){
20561         return Roo.dd.Registry.getHandleFromEvent(e);
20562     },
20563     
20564     /**
20565      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20566      * this.dragData.ddel
20567      * @param {Number} x The x position of the click on the dragged object
20568      * @param {Number} y The y position of the click on the dragged object
20569      * @return {Boolean} true to continue the drag, false to cancel
20570      */
20571     onInitDrag : function(x, y){
20572         this.proxy.update(this.dragData.ddel.cloneNode(true));
20573         this.onStartDrag(x, y);
20574         return true;
20575     },
20576     
20577     /**
20578      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20579      */
20580     afterRepair : function(){
20581         if(Roo.enableFx){
20582             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20583         }
20584         this.dragging = false;
20585     },
20586
20587     /**
20588      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20589      * the XY of this.dragData.ddel
20590      * @param {EventObject} e The mouse up event
20591      * @return {Array} The xy location (e.g. [100, 200])
20592      */
20593     getRepairXY : function(e){
20594         return Roo.Element.fly(this.dragData.ddel).getXY();  
20595     }
20596 });/*
20597  * Based on:
20598  * Ext JS Library 1.1.1
20599  * Copyright(c) 2006-2007, Ext JS, LLC.
20600  *
20601  * Originally Released Under LGPL - original licence link has changed is not relivant.
20602  *
20603  * Fork - LGPL
20604  * <script type="text/javascript">
20605  */
20606 /**
20607  * @class Roo.dd.DropZone
20608  * @extends Roo.dd.DropTarget
20609  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20610  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20611  * @constructor
20612  * @param {String/HTMLElement/Element} el The container element
20613  * @param {Object} config
20614  */
20615 Roo.dd.DropZone = function(el, config){
20616     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20617 };
20618
20619 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20620     /**
20621      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20622      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20623      * provide your own custom lookup.
20624      * @param {Event} e The event
20625      * @return {Object} data The custom data
20626      */
20627     getTargetFromEvent : function(e){
20628         return Roo.dd.Registry.getTargetFromEvent(e);
20629     },
20630
20631     /**
20632      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20633      * that it has registered.  This method has no default implementation and should be overridden to provide
20634      * node-specific processing if necessary.
20635      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20636      * {@link #getTargetFromEvent} for this node)
20637      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20638      * @param {Event} e The event
20639      * @param {Object} data An object containing arbitrary data supplied by the drag source
20640      */
20641     onNodeEnter : function(n, dd, e, data){
20642         
20643     },
20644
20645     /**
20646      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20647      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20648      * overridden to provide the proper feedback.
20649      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20650      * {@link #getTargetFromEvent} for this node)
20651      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20652      * @param {Event} e The event
20653      * @param {Object} data An object containing arbitrary data supplied by the drag source
20654      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20655      * underlying {@link Roo.dd.StatusProxy} can be updated
20656      */
20657     onNodeOver : function(n, dd, e, data){
20658         return this.dropAllowed;
20659     },
20660
20661     /**
20662      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20663      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20664      * node-specific processing if necessary.
20665      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20666      * {@link #getTargetFromEvent} for this node)
20667      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20668      * @param {Event} e The event
20669      * @param {Object} data An object containing arbitrary data supplied by the drag source
20670      */
20671     onNodeOut : function(n, dd, e, data){
20672         
20673     },
20674
20675     /**
20676      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20677      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20678      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20679      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20680      * {@link #getTargetFromEvent} for this node)
20681      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20682      * @param {Event} e The event
20683      * @param {Object} data An object containing arbitrary data supplied by the drag source
20684      * @return {Boolean} True if the drop was valid, else false
20685      */
20686     onNodeDrop : function(n, dd, e, data){
20687         return false;
20688     },
20689
20690     /**
20691      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20692      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20693      * it should be overridden to provide the proper feedback if necessary.
20694      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20695      * @param {Event} e The event
20696      * @param {Object} data An object containing arbitrary data supplied by the drag source
20697      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20698      * underlying {@link Roo.dd.StatusProxy} can be updated
20699      */
20700     onContainerOver : function(dd, e, data){
20701         return this.dropNotAllowed;
20702     },
20703
20704     /**
20705      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20706      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20707      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20708      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20709      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20710      * @param {Event} e The event
20711      * @param {Object} data An object containing arbitrary data supplied by the drag source
20712      * @return {Boolean} True if the drop was valid, else false
20713      */
20714     onContainerDrop : function(dd, e, data){
20715         return false;
20716     },
20717
20718     /**
20719      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20720      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20721      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20722      * you should override this method and provide a custom implementation.
20723      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20724      * @param {Event} e The event
20725      * @param {Object} data An object containing arbitrary data supplied by the drag source
20726      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20727      * underlying {@link Roo.dd.StatusProxy} can be updated
20728      */
20729     notifyEnter : function(dd, e, data){
20730         return this.dropNotAllowed;
20731     },
20732
20733     /**
20734      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20735      * This method will be called on every mouse movement while the drag source is over the drop zone.
20736      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20737      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20738      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20739      * registered node, it will call {@link #onContainerOver}.
20740      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20741      * @param {Event} e The event
20742      * @param {Object} data An object containing arbitrary data supplied by the drag source
20743      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20744      * underlying {@link Roo.dd.StatusProxy} can be updated
20745      */
20746     notifyOver : function(dd, e, data){
20747         var n = this.getTargetFromEvent(e);
20748         if(!n){ // not over valid drop target
20749             if(this.lastOverNode){
20750                 this.onNodeOut(this.lastOverNode, dd, e, data);
20751                 this.lastOverNode = null;
20752             }
20753             return this.onContainerOver(dd, e, data);
20754         }
20755         if(this.lastOverNode != n){
20756             if(this.lastOverNode){
20757                 this.onNodeOut(this.lastOverNode, dd, e, data);
20758             }
20759             this.onNodeEnter(n, dd, e, data);
20760             this.lastOverNode = n;
20761         }
20762         return this.onNodeOver(n, dd, e, data);
20763     },
20764
20765     /**
20766      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20767      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20768      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20769      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20770      * @param {Event} e The event
20771      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20772      */
20773     notifyOut : function(dd, e, data){
20774         if(this.lastOverNode){
20775             this.onNodeOut(this.lastOverNode, dd, e, data);
20776             this.lastOverNode = null;
20777         }
20778     },
20779
20780     /**
20781      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20782      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20783      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20784      * otherwise it will call {@link #onContainerDrop}.
20785      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20786      * @param {Event} e The event
20787      * @param {Object} data An object containing arbitrary data supplied by the drag source
20788      * @return {Boolean} True if the drop was valid, else false
20789      */
20790     notifyDrop : function(dd, e, data){
20791         if(this.lastOverNode){
20792             this.onNodeOut(this.lastOverNode, dd, e, data);
20793             this.lastOverNode = null;
20794         }
20795         var n = this.getTargetFromEvent(e);
20796         return n ?
20797             this.onNodeDrop(n, dd, e, data) :
20798             this.onContainerDrop(dd, e, data);
20799     },
20800
20801     // private
20802     triggerCacheRefresh : function(){
20803         Roo.dd.DDM.refreshCache(this.groups);
20804     }  
20805 });/*
20806  * Based on:
20807  * Ext JS Library 1.1.1
20808  * Copyright(c) 2006-2007, Ext JS, LLC.
20809  *
20810  * Originally Released Under LGPL - original licence link has changed is not relivant.
20811  *
20812  * Fork - LGPL
20813  * <script type="text/javascript">
20814  */
20815
20816
20817 /**
20818  * @class Roo.data.SortTypes
20819  * @singleton
20820  * Defines the default sorting (casting?) comparison functions used when sorting data.
20821  */
20822 Roo.data.SortTypes = {
20823     /**
20824      * Default sort that does nothing
20825      * @param {Mixed} s The value being converted
20826      * @return {Mixed} The comparison value
20827      */
20828     none : function(s){
20829         return s;
20830     },
20831     
20832     /**
20833      * The regular expression used to strip tags
20834      * @type {RegExp}
20835      * @property
20836      */
20837     stripTagsRE : /<\/?[^>]+>/gi,
20838     
20839     /**
20840      * Strips all HTML tags to sort on text only
20841      * @param {Mixed} s The value being converted
20842      * @return {String} The comparison value
20843      */
20844     asText : function(s){
20845         return String(s).replace(this.stripTagsRE, "");
20846     },
20847     
20848     /**
20849      * Strips all HTML tags to sort on text only - Case insensitive
20850      * @param {Mixed} s The value being converted
20851      * @return {String} The comparison value
20852      */
20853     asUCText : function(s){
20854         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20855     },
20856     
20857     /**
20858      * Case insensitive string
20859      * @param {Mixed} s The value being converted
20860      * @return {String} The comparison value
20861      */
20862     asUCString : function(s) {
20863         return String(s).toUpperCase();
20864     },
20865     
20866     /**
20867      * Date sorting
20868      * @param {Mixed} s The value being converted
20869      * @return {Number} The comparison value
20870      */
20871     asDate : function(s) {
20872         if(!s){
20873             return 0;
20874         }
20875         if(s instanceof Date){
20876             return s.getTime();
20877         }
20878         return Date.parse(String(s));
20879     },
20880     
20881     /**
20882      * Float sorting
20883      * @param {Mixed} s The value being converted
20884      * @return {Float} The comparison value
20885      */
20886     asFloat : function(s) {
20887         var val = parseFloat(String(s).replace(/,/g, ""));
20888         if(isNaN(val)) val = 0;
20889         return val;
20890     },
20891     
20892     /**
20893      * Integer sorting
20894      * @param {Mixed} s The value being converted
20895      * @return {Number} The comparison value
20896      */
20897     asInt : function(s) {
20898         var val = parseInt(String(s).replace(/,/g, ""));
20899         if(isNaN(val)) val = 0;
20900         return val;
20901     }
20902 };/*
20903  * Based on:
20904  * Ext JS Library 1.1.1
20905  * Copyright(c) 2006-2007, Ext JS, LLC.
20906  *
20907  * Originally Released Under LGPL - original licence link has changed is not relivant.
20908  *
20909  * Fork - LGPL
20910  * <script type="text/javascript">
20911  */
20912
20913 /**
20914 * @class Roo.data.Record
20915  * Instances of this class encapsulate both record <em>definition</em> information, and record
20916  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20917  * to access Records cached in an {@link Roo.data.Store} object.<br>
20918  * <p>
20919  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20920  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20921  * objects.<br>
20922  * <p>
20923  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20924  * @constructor
20925  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20926  * {@link #create}. The parameters are the same.
20927  * @param {Array} data An associative Array of data values keyed by the field name.
20928  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20929  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20930  * not specified an integer id is generated.
20931  */
20932 Roo.data.Record = function(data, id){
20933     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20934     this.data = data;
20935 };
20936
20937 /**
20938  * Generate a constructor for a specific record layout.
20939  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20940  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20941  * Each field definition object may contain the following properties: <ul>
20942  * <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,
20943  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20944  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20945  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20946  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20947  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20948  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20949  * this may be omitted.</p></li>
20950  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20951  * <ul><li>auto (Default, implies no conversion)</li>
20952  * <li>string</li>
20953  * <li>int</li>
20954  * <li>float</li>
20955  * <li>boolean</li>
20956  * <li>date</li></ul></p></li>
20957  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20958  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20959  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20960  * by the Reader into an object that will be stored in the Record. It is passed the
20961  * following parameters:<ul>
20962  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20963  * </ul></p></li>
20964  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20965  * </ul>
20966  * <br>usage:<br><pre><code>
20967 var TopicRecord = Roo.data.Record.create(
20968     {name: 'title', mapping: 'topic_title'},
20969     {name: 'author', mapping: 'username'},
20970     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20971     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20972     {name: 'lastPoster', mapping: 'user2'},
20973     {name: 'excerpt', mapping: 'post_text'}
20974 );
20975
20976 var myNewRecord = new TopicRecord({
20977     title: 'Do my job please',
20978     author: 'noobie',
20979     totalPosts: 1,
20980     lastPost: new Date(),
20981     lastPoster: 'Animal',
20982     excerpt: 'No way dude!'
20983 });
20984 myStore.add(myNewRecord);
20985 </code></pre>
20986  * @method create
20987  * @static
20988  */
20989 Roo.data.Record.create = function(o){
20990     var f = function(){
20991         f.superclass.constructor.apply(this, arguments);
20992     };
20993     Roo.extend(f, Roo.data.Record);
20994     var p = f.prototype;
20995     p.fields = new Roo.util.MixedCollection(false, function(field){
20996         return field.name;
20997     });
20998     for(var i = 0, len = o.length; i < len; i++){
20999         p.fields.add(new Roo.data.Field(o[i]));
21000     }
21001     f.getField = function(name){
21002         return p.fields.get(name);  
21003     };
21004     return f;
21005 };
21006
21007 Roo.data.Record.AUTO_ID = 1000;
21008 Roo.data.Record.EDIT = 'edit';
21009 Roo.data.Record.REJECT = 'reject';
21010 Roo.data.Record.COMMIT = 'commit';
21011
21012 Roo.data.Record.prototype = {
21013     /**
21014      * Readonly flag - true if this record has been modified.
21015      * @type Boolean
21016      */
21017     dirty : false,
21018     editing : false,
21019     error: null,
21020     modified: null,
21021
21022     // private
21023     join : function(store){
21024         this.store = store;
21025     },
21026
21027     /**
21028      * Set the named field to the specified value.
21029      * @param {String} name The name of the field to set.
21030      * @param {Object} value The value to set the field to.
21031      */
21032     set : function(name, value){
21033         if(this.data[name] == value){
21034             return;
21035         }
21036         this.dirty = true;
21037         if(!this.modified){
21038             this.modified = {};
21039         }
21040         if(typeof this.modified[name] == 'undefined'){
21041             this.modified[name] = this.data[name];
21042         }
21043         this.data[name] = value;
21044         if(!this.editing && this.store){
21045             this.store.afterEdit(this);
21046         }       
21047     },
21048
21049     /**
21050      * Get the value of the named field.
21051      * @param {String} name The name of the field to get the value of.
21052      * @return {Object} The value of the field.
21053      */
21054     get : function(name){
21055         return this.data[name]; 
21056     },
21057
21058     // private
21059     beginEdit : function(){
21060         this.editing = true;
21061         this.modified = {}; 
21062     },
21063
21064     // private
21065     cancelEdit : function(){
21066         this.editing = false;
21067         delete this.modified;
21068     },
21069
21070     // private
21071     endEdit : function(){
21072         this.editing = false;
21073         if(this.dirty && this.store){
21074             this.store.afterEdit(this);
21075         }
21076     },
21077
21078     /**
21079      * Usually called by the {@link Roo.data.Store} which owns the Record.
21080      * Rejects all changes made to the Record since either creation, or the last commit operation.
21081      * Modified fields are reverted to their original values.
21082      * <p>
21083      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21084      * of reject operations.
21085      */
21086     reject : function(){
21087         var m = this.modified;
21088         for(var n in m){
21089             if(typeof m[n] != "function"){
21090                 this.data[n] = m[n];
21091             }
21092         }
21093         this.dirty = false;
21094         delete this.modified;
21095         this.editing = false;
21096         if(this.store){
21097             this.store.afterReject(this);
21098         }
21099     },
21100
21101     /**
21102      * Usually called by the {@link Roo.data.Store} which owns the Record.
21103      * Commits all changes made to the Record since either creation, or the last commit operation.
21104      * <p>
21105      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21106      * of commit operations.
21107      */
21108     commit : function(){
21109         this.dirty = false;
21110         delete this.modified;
21111         this.editing = false;
21112         if(this.store){
21113             this.store.afterCommit(this);
21114         }
21115     },
21116
21117     // private
21118     hasError : function(){
21119         return this.error != null;
21120     },
21121
21122     // private
21123     clearError : function(){
21124         this.error = null;
21125     },
21126
21127     /**
21128      * Creates a copy of this record.
21129      * @param {String} id (optional) A new record id if you don't want to use this record's id
21130      * @return {Record}
21131      */
21132     copy : function(newId) {
21133         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21134     }
21135 };/*
21136  * Based on:
21137  * Ext JS Library 1.1.1
21138  * Copyright(c) 2006-2007, Ext JS, LLC.
21139  *
21140  * Originally Released Under LGPL - original licence link has changed is not relivant.
21141  *
21142  * Fork - LGPL
21143  * <script type="text/javascript">
21144  */
21145
21146
21147
21148 /**
21149  * @class Roo.data.Store
21150  * @extends Roo.util.Observable
21151  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21152  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21153  * <p>
21154  * 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
21155  * has no knowledge of the format of the data returned by the Proxy.<br>
21156  * <p>
21157  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21158  * instances from the data object. These records are cached and made available through accessor functions.
21159  * @constructor
21160  * Creates a new Store.
21161  * @param {Object} config A config object containing the objects needed for the Store to access data,
21162  * and read the data into Records.
21163  */
21164 Roo.data.Store = function(config){
21165     this.data = new Roo.util.MixedCollection(false);
21166     this.data.getKey = function(o){
21167         return o.id;
21168     };
21169     this.baseParams = {};
21170     // private
21171     this.paramNames = {
21172         "start" : "start",
21173         "limit" : "limit",
21174         "sort" : "sort",
21175         "dir" : "dir",
21176         "multisort" : "_multisort"
21177     };
21178
21179     if(config && config.data){
21180         this.inlineData = config.data;
21181         delete config.data;
21182     }
21183
21184     Roo.apply(this, config);
21185     
21186     if(this.reader){ // reader passed
21187         this.reader = Roo.factory(this.reader, Roo.data);
21188         this.reader.xmodule = this.xmodule || false;
21189         if(!this.recordType){
21190             this.recordType = this.reader.recordType;
21191         }
21192         if(this.reader.onMetaChange){
21193             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21194         }
21195     }
21196
21197     if(this.recordType){
21198         this.fields = this.recordType.prototype.fields;
21199     }
21200     this.modified = [];
21201
21202     this.addEvents({
21203         /**
21204          * @event datachanged
21205          * Fires when the data cache has changed, and a widget which is using this Store
21206          * as a Record cache should refresh its view.
21207          * @param {Store} this
21208          */
21209         datachanged : true,
21210         /**
21211          * @event metachange
21212          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21213          * @param {Store} this
21214          * @param {Object} meta The JSON metadata
21215          */
21216         metachange : true,
21217         /**
21218          * @event add
21219          * Fires when Records have been added to the Store
21220          * @param {Store} this
21221          * @param {Roo.data.Record[]} records The array of Records added
21222          * @param {Number} index The index at which the record(s) were added
21223          */
21224         add : true,
21225         /**
21226          * @event remove
21227          * Fires when a Record has been removed from the Store
21228          * @param {Store} this
21229          * @param {Roo.data.Record} record The Record that was removed
21230          * @param {Number} index The index at which the record was removed
21231          */
21232         remove : true,
21233         /**
21234          * @event update
21235          * Fires when a Record has been updated
21236          * @param {Store} this
21237          * @param {Roo.data.Record} record The Record that was updated
21238          * @param {String} operation The update operation being performed.  Value may be one of:
21239          * <pre><code>
21240  Roo.data.Record.EDIT
21241  Roo.data.Record.REJECT
21242  Roo.data.Record.COMMIT
21243          * </code></pre>
21244          */
21245         update : true,
21246         /**
21247          * @event clear
21248          * Fires when the data cache has been cleared.
21249          * @param {Store} this
21250          */
21251         clear : true,
21252         /**
21253          * @event beforeload
21254          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21255          * the load action will be canceled.
21256          * @param {Store} this
21257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21258          */
21259         beforeload : true,
21260         /**
21261          * @event beforeloadadd
21262          * Fires after a new set of Records has been loaded.
21263          * @param {Store} this
21264          * @param {Roo.data.Record[]} records The Records that were loaded
21265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21266          */
21267         beforeloadadd : true,
21268         /**
21269          * @event load
21270          * Fires after a new set of Records has been loaded, before they are added to the store.
21271          * @param {Store} this
21272          * @param {Roo.data.Record[]} records The Records that were loaded
21273          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21274          * @params {Object} return from reader
21275          */
21276         load : true,
21277         /**
21278          * @event loadexception
21279          * Fires if an exception occurs in the Proxy during loading.
21280          * Called with the signature of the Proxy's "loadexception" event.
21281          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21282          * 
21283          * @param {Proxy} 
21284          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21285          * @param {Object} load options 
21286          * @param {Object} jsonData from your request (normally this contains the Exception)
21287          */
21288         loadexception : true
21289     });
21290     
21291     if(this.proxy){
21292         this.proxy = Roo.factory(this.proxy, Roo.data);
21293         this.proxy.xmodule = this.xmodule || false;
21294         this.relayEvents(this.proxy,  ["loadexception"]);
21295     }
21296     this.sortToggle = {};
21297     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21298
21299     Roo.data.Store.superclass.constructor.call(this);
21300
21301     if(this.inlineData){
21302         this.loadData(this.inlineData);
21303         delete this.inlineData;
21304     }
21305 };
21306
21307 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21308      /**
21309     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21310     * without a remote query - used by combo/forms at present.
21311     */
21312     
21313     /**
21314     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21315     */
21316     /**
21317     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21318     */
21319     /**
21320     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21321     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21322     */
21323     /**
21324     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21325     * on any HTTP request
21326     */
21327     /**
21328     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21329     */
21330     /**
21331     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21332     */
21333     multiSort: false,
21334     /**
21335     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21336     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21337     */
21338     remoteSort : false,
21339
21340     /**
21341     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21342      * loaded or when a record is removed. (defaults to false).
21343     */
21344     pruneModifiedRecords : false,
21345
21346     // private
21347     lastOptions : null,
21348
21349     /**
21350      * Add Records to the Store and fires the add event.
21351      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21352      */
21353     add : function(records){
21354         records = [].concat(records);
21355         for(var i = 0, len = records.length; i < len; i++){
21356             records[i].join(this);
21357         }
21358         var index = this.data.length;
21359         this.data.addAll(records);
21360         this.fireEvent("add", this, records, index);
21361     },
21362
21363     /**
21364      * Remove a Record from the Store and fires the remove event.
21365      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21366      */
21367     remove : function(record){
21368         var index = this.data.indexOf(record);
21369         this.data.removeAt(index);
21370         if(this.pruneModifiedRecords){
21371             this.modified.remove(record);
21372         }
21373         this.fireEvent("remove", this, record, index);
21374     },
21375
21376     /**
21377      * Remove all Records from the Store and fires the clear event.
21378      */
21379     removeAll : function(){
21380         this.data.clear();
21381         if(this.pruneModifiedRecords){
21382             this.modified = [];
21383         }
21384         this.fireEvent("clear", this);
21385     },
21386
21387     /**
21388      * Inserts Records to the Store at the given index and fires the add event.
21389      * @param {Number} index The start index at which to insert the passed Records.
21390      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21391      */
21392     insert : function(index, records){
21393         records = [].concat(records);
21394         for(var i = 0, len = records.length; i < len; i++){
21395             this.data.insert(index, records[i]);
21396             records[i].join(this);
21397         }
21398         this.fireEvent("add", this, records, index);
21399     },
21400
21401     /**
21402      * Get the index within the cache of the passed Record.
21403      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21404      * @return {Number} The index of the passed Record. Returns -1 if not found.
21405      */
21406     indexOf : function(record){
21407         return this.data.indexOf(record);
21408     },
21409
21410     /**
21411      * Get the index within the cache of the Record with the passed id.
21412      * @param {String} id The id of the Record to find.
21413      * @return {Number} The index of the Record. Returns -1 if not found.
21414      */
21415     indexOfId : function(id){
21416         return this.data.indexOfKey(id);
21417     },
21418
21419     /**
21420      * Get the Record with the specified id.
21421      * @param {String} id The id of the Record to find.
21422      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21423      */
21424     getById : function(id){
21425         return this.data.key(id);
21426     },
21427
21428     /**
21429      * Get the Record at the specified index.
21430      * @param {Number} index The index of the Record to find.
21431      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21432      */
21433     getAt : function(index){
21434         return this.data.itemAt(index);
21435     },
21436
21437     /**
21438      * Returns a range of Records between specified indices.
21439      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21440      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21441      * @return {Roo.data.Record[]} An array of Records
21442      */
21443     getRange : function(start, end){
21444         return this.data.getRange(start, end);
21445     },
21446
21447     // private
21448     storeOptions : function(o){
21449         o = Roo.apply({}, o);
21450         delete o.callback;
21451         delete o.scope;
21452         this.lastOptions = o;
21453     },
21454
21455     /**
21456      * Loads the Record cache from the configured Proxy using the configured Reader.
21457      * <p>
21458      * If using remote paging, then the first load call must specify the <em>start</em>
21459      * and <em>limit</em> properties in the options.params property to establish the initial
21460      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21461      * <p>
21462      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21463      * and this call will return before the new data has been loaded. Perform any post-processing
21464      * in a callback function, or in a "load" event handler.</strong>
21465      * <p>
21466      * @param {Object} options An object containing properties which control loading options:<ul>
21467      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21468      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21469      * passed the following arguments:<ul>
21470      * <li>r : Roo.data.Record[]</li>
21471      * <li>options: Options object from the load call</li>
21472      * <li>success: Boolean success indicator</li></ul></li>
21473      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21474      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21475      * </ul>
21476      */
21477     load : function(options){
21478         options = options || {};
21479         if(this.fireEvent("beforeload", this, options) !== false){
21480             this.storeOptions(options);
21481             var p = Roo.apply(options.params || {}, this.baseParams);
21482             // if meta was not loaded from remote source.. try requesting it.
21483             if (!this.reader.metaFromRemote) {
21484                 p._requestMeta = 1;
21485             }
21486             if(this.sortInfo && this.remoteSort){
21487                 var pn = this.paramNames;
21488                 p[pn["sort"]] = this.sortInfo.field;
21489                 p[pn["dir"]] = this.sortInfo.direction;
21490             }
21491             if (this.multiSort) {
21492                 var pn = this.paramNames;
21493                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21494             }
21495             
21496             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21497         }
21498     },
21499
21500     /**
21501      * Reloads the Record cache from the configured Proxy using the configured Reader and
21502      * the options from the last load operation performed.
21503      * @param {Object} options (optional) An object containing properties which may override the options
21504      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21505      * the most recently used options are reused).
21506      */
21507     reload : function(options){
21508         this.load(Roo.applyIf(options||{}, this.lastOptions));
21509     },
21510
21511     // private
21512     // Called as a callback by the Reader during a load operation.
21513     loadRecords : function(o, options, success){
21514         if(!o || success === false){
21515             if(success !== false){
21516                 this.fireEvent("load", this, [], options, o);
21517             }
21518             if(options.callback){
21519                 options.callback.call(options.scope || this, [], options, false);
21520             }
21521             return;
21522         }
21523         // if data returned failure - throw an exception.
21524         if (o.success === false) {
21525             // show a message if no listener is registered.
21526             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21527                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21528             }
21529             // loadmask wil be hooked into this..
21530             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21531             return;
21532         }
21533         var r = o.records, t = o.totalRecords || r.length;
21534         
21535         this.fireEvent("beforeloadadd", this, r, options, o);
21536         
21537         if(!options || options.add !== true){
21538             if(this.pruneModifiedRecords){
21539                 this.modified = [];
21540             }
21541             for(var i = 0, len = r.length; i < len; i++){
21542                 r[i].join(this);
21543             }
21544             if(this.snapshot){
21545                 this.data = this.snapshot;
21546                 delete this.snapshot;
21547             }
21548             this.data.clear();
21549             this.data.addAll(r);
21550             this.totalLength = t;
21551             this.applySort();
21552             this.fireEvent("datachanged", this);
21553         }else{
21554             this.totalLength = Math.max(t, this.data.length+r.length);
21555             this.add(r);
21556         }
21557         this.fireEvent("load", this, r, options, o);
21558         if(options.callback){
21559             options.callback.call(options.scope || this, r, options, true);
21560         }
21561     },
21562
21563
21564     /**
21565      * Loads data from a passed data block. A Reader which understands the format of the data
21566      * must have been configured in the constructor.
21567      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21568      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21569      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21570      */
21571     loadData : function(o, append){
21572         var r = this.reader.readRecords(o);
21573         this.loadRecords(r, {add: append}, true);
21574     },
21575
21576     /**
21577      * Gets the number of cached records.
21578      * <p>
21579      * <em>If using paging, this may not be the total size of the dataset. If the data object
21580      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21581      * the data set size</em>
21582      */
21583     getCount : function(){
21584         return this.data.length || 0;
21585     },
21586
21587     /**
21588      * Gets the total number of records in the dataset as returned by the server.
21589      * <p>
21590      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21591      * the dataset size</em>
21592      */
21593     getTotalCount : function(){
21594         return this.totalLength || 0;
21595     },
21596
21597     /**
21598      * Returns the sort state of the Store as an object with two properties:
21599      * <pre><code>
21600  field {String} The name of the field by which the Records are sorted
21601  direction {String} The sort order, "ASC" or "DESC"
21602      * </code></pre>
21603      */
21604     getSortState : function(){
21605         return this.sortInfo;
21606     },
21607
21608     // private
21609     applySort : function(){
21610         if(this.sortInfo && !this.remoteSort){
21611             var s = this.sortInfo, f = s.field;
21612             var st = this.fields.get(f).sortType;
21613             var fn = function(r1, r2){
21614                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21615                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21616             };
21617             this.data.sort(s.direction, fn);
21618             if(this.snapshot && this.snapshot != this.data){
21619                 this.snapshot.sort(s.direction, fn);
21620             }
21621         }
21622     },
21623
21624     /**
21625      * Sets the default sort column and order to be used by the next load operation.
21626      * @param {String} fieldName The name of the field to sort by.
21627      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21628      */
21629     setDefaultSort : function(field, dir){
21630         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21631     },
21632
21633     /**
21634      * Sort the Records.
21635      * If remote sorting is used, the sort is performed on the server, and the cache is
21636      * reloaded. If local sorting is used, the cache is sorted internally.
21637      * @param {String} fieldName The name of the field to sort by.
21638      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21639      */
21640     sort : function(fieldName, dir){
21641         var f = this.fields.get(fieldName);
21642         if(!dir){
21643             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21644             
21645             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21646                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21647             }else{
21648                 dir = f.sortDir;
21649             }
21650         }
21651         this.sortToggle[f.name] = dir;
21652         this.sortInfo = {field: f.name, direction: dir};
21653         if(!this.remoteSort){
21654             this.applySort();
21655             this.fireEvent("datachanged", this);
21656         }else{
21657             this.load(this.lastOptions);
21658         }
21659     },
21660
21661     /**
21662      * Calls the specified function for each of the Records in the cache.
21663      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21664      * Returning <em>false</em> aborts and exits the iteration.
21665      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21666      */
21667     each : function(fn, scope){
21668         this.data.each(fn, scope);
21669     },
21670
21671     /**
21672      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21673      * (e.g., during paging).
21674      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21675      */
21676     getModifiedRecords : function(){
21677         return this.modified;
21678     },
21679
21680     // private
21681     createFilterFn : function(property, value, anyMatch){
21682         if(!value.exec){ // not a regex
21683             value = String(value);
21684             if(value.length == 0){
21685                 return false;
21686             }
21687             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21688         }
21689         return function(r){
21690             return value.test(r.data[property]);
21691         };
21692     },
21693
21694     /**
21695      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21696      * @param {String} property A field on your records
21697      * @param {Number} start The record index to start at (defaults to 0)
21698      * @param {Number} end The last record index to include (defaults to length - 1)
21699      * @return {Number} The sum
21700      */
21701     sum : function(property, start, end){
21702         var rs = this.data.items, v = 0;
21703         start = start || 0;
21704         end = (end || end === 0) ? end : rs.length-1;
21705
21706         for(var i = start; i <= end; i++){
21707             v += (rs[i].data[property] || 0);
21708         }
21709         return v;
21710     },
21711
21712     /**
21713      * Filter the records by a specified property.
21714      * @param {String} field A field on your records
21715      * @param {String/RegExp} value Either a string that the field
21716      * should start with or a RegExp to test against the field
21717      * @param {Boolean} anyMatch True to match any part not just the beginning
21718      */
21719     filter : function(property, value, anyMatch){
21720         var fn = this.createFilterFn(property, value, anyMatch);
21721         return fn ? this.filterBy(fn) : this.clearFilter();
21722     },
21723
21724     /**
21725      * Filter by a function. The specified function will be called with each
21726      * record in this data source. If the function returns true the record is included,
21727      * otherwise it is filtered.
21728      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21729      * @param {Object} scope (optional) The scope of the function (defaults to this)
21730      */
21731     filterBy : function(fn, scope){
21732         this.snapshot = this.snapshot || this.data;
21733         this.data = this.queryBy(fn, scope||this);
21734         this.fireEvent("datachanged", this);
21735     },
21736
21737     /**
21738      * Query the records by a specified property.
21739      * @param {String} field A field on your records
21740      * @param {String/RegExp} value Either a string that the field
21741      * should start with or a RegExp to test against the field
21742      * @param {Boolean} anyMatch True to match any part not just the beginning
21743      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21744      */
21745     query : function(property, value, anyMatch){
21746         var fn = this.createFilterFn(property, value, anyMatch);
21747         return fn ? this.queryBy(fn) : this.data.clone();
21748     },
21749
21750     /**
21751      * Query by a function. The specified function will be called with each
21752      * record in this data source. If the function returns true the record is included
21753      * in the results.
21754      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21755      * @param {Object} scope (optional) The scope of the function (defaults to this)
21756       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21757      **/
21758     queryBy : function(fn, scope){
21759         var data = this.snapshot || this.data;
21760         return data.filterBy(fn, scope||this);
21761     },
21762
21763     /**
21764      * Collects unique values for a particular dataIndex from this store.
21765      * @param {String} dataIndex The property to collect
21766      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21767      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21768      * @return {Array} An array of the unique values
21769      **/
21770     collect : function(dataIndex, allowNull, bypassFilter){
21771         var d = (bypassFilter === true && this.snapshot) ?
21772                 this.snapshot.items : this.data.items;
21773         var v, sv, r = [], l = {};
21774         for(var i = 0, len = d.length; i < len; i++){
21775             v = d[i].data[dataIndex];
21776             sv = String(v);
21777             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21778                 l[sv] = true;
21779                 r[r.length] = v;
21780             }
21781         }
21782         return r;
21783     },
21784
21785     /**
21786      * Revert to a view of the Record cache with no filtering applied.
21787      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21788      */
21789     clearFilter : function(suppressEvent){
21790         if(this.snapshot && this.snapshot != this.data){
21791             this.data = this.snapshot;
21792             delete this.snapshot;
21793             if(suppressEvent !== true){
21794                 this.fireEvent("datachanged", this);
21795             }
21796         }
21797     },
21798
21799     // private
21800     afterEdit : function(record){
21801         if(this.modified.indexOf(record) == -1){
21802             this.modified.push(record);
21803         }
21804         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21805     },
21806     
21807     // private
21808     afterReject : function(record){
21809         this.modified.remove(record);
21810         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21811     },
21812
21813     // private
21814     afterCommit : function(record){
21815         this.modified.remove(record);
21816         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21817     },
21818
21819     /**
21820      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21821      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21822      */
21823     commitChanges : function(){
21824         var m = this.modified.slice(0);
21825         this.modified = [];
21826         for(var i = 0, len = m.length; i < len; i++){
21827             m[i].commit();
21828         }
21829     },
21830
21831     /**
21832      * Cancel outstanding changes on all changed records.
21833      */
21834     rejectChanges : function(){
21835         var m = this.modified.slice(0);
21836         this.modified = [];
21837         for(var i = 0, len = m.length; i < len; i++){
21838             m[i].reject();
21839         }
21840     },
21841
21842     onMetaChange : function(meta, rtype, o){
21843         this.recordType = rtype;
21844         this.fields = rtype.prototype.fields;
21845         delete this.snapshot;
21846         this.sortInfo = meta.sortInfo || this.sortInfo;
21847         this.modified = [];
21848         this.fireEvent('metachange', this, this.reader.meta);
21849     },
21850     
21851     moveIndex : function(data, type)
21852     {
21853         var index = this.indexOf(data);
21854         
21855         var newIndex = index + type;
21856         
21857         this.remove(data);
21858         
21859         this.insert(newIndex, data);
21860         
21861     }
21862 });/*
21863  * Based on:
21864  * Ext JS Library 1.1.1
21865  * Copyright(c) 2006-2007, Ext JS, LLC.
21866  *
21867  * Originally Released Under LGPL - original licence link has changed is not relivant.
21868  *
21869  * Fork - LGPL
21870  * <script type="text/javascript">
21871  */
21872
21873 /**
21874  * @class Roo.data.SimpleStore
21875  * @extends Roo.data.Store
21876  * Small helper class to make creating Stores from Array data easier.
21877  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21878  * @cfg {Array} fields An array of field definition objects, or field name strings.
21879  * @cfg {Array} data The multi-dimensional array of data
21880  * @constructor
21881  * @param {Object} config
21882  */
21883 Roo.data.SimpleStore = function(config){
21884     Roo.data.SimpleStore.superclass.constructor.call(this, {
21885         isLocal : true,
21886         reader: new Roo.data.ArrayReader({
21887                 id: config.id
21888             },
21889             Roo.data.Record.create(config.fields)
21890         ),
21891         proxy : new Roo.data.MemoryProxy(config.data)
21892     });
21893     this.load();
21894 };
21895 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21896  * Based on:
21897  * Ext JS Library 1.1.1
21898  * Copyright(c) 2006-2007, Ext JS, LLC.
21899  *
21900  * Originally Released Under LGPL - original licence link has changed is not relivant.
21901  *
21902  * Fork - LGPL
21903  * <script type="text/javascript">
21904  */
21905
21906 /**
21907 /**
21908  * @extends Roo.data.Store
21909  * @class Roo.data.JsonStore
21910  * Small helper class to make creating Stores for JSON data easier. <br/>
21911 <pre><code>
21912 var store = new Roo.data.JsonStore({
21913     url: 'get-images.php',
21914     root: 'images',
21915     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21916 });
21917 </code></pre>
21918  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21919  * JsonReader and HttpProxy (unless inline data is provided).</b>
21920  * @cfg {Array} fields An array of field definition objects, or field name strings.
21921  * @constructor
21922  * @param {Object} config
21923  */
21924 Roo.data.JsonStore = function(c){
21925     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21926         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21927         reader: new Roo.data.JsonReader(c, c.fields)
21928     }));
21929 };
21930 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21931  * Based on:
21932  * Ext JS Library 1.1.1
21933  * Copyright(c) 2006-2007, Ext JS, LLC.
21934  *
21935  * Originally Released Under LGPL - original licence link has changed is not relivant.
21936  *
21937  * Fork - LGPL
21938  * <script type="text/javascript">
21939  */
21940
21941  
21942 Roo.data.Field = function(config){
21943     if(typeof config == "string"){
21944         config = {name: config};
21945     }
21946     Roo.apply(this, config);
21947     
21948     if(!this.type){
21949         this.type = "auto";
21950     }
21951     
21952     var st = Roo.data.SortTypes;
21953     // named sortTypes are supported, here we look them up
21954     if(typeof this.sortType == "string"){
21955         this.sortType = st[this.sortType];
21956     }
21957     
21958     // set default sortType for strings and dates
21959     if(!this.sortType){
21960         switch(this.type){
21961             case "string":
21962                 this.sortType = st.asUCString;
21963                 break;
21964             case "date":
21965                 this.sortType = st.asDate;
21966                 break;
21967             default:
21968                 this.sortType = st.none;
21969         }
21970     }
21971
21972     // define once
21973     var stripRe = /[\$,%]/g;
21974
21975     // prebuilt conversion function for this field, instead of
21976     // switching every time we're reading a value
21977     if(!this.convert){
21978         var cv, dateFormat = this.dateFormat;
21979         switch(this.type){
21980             case "":
21981             case "auto":
21982             case undefined:
21983                 cv = function(v){ return v; };
21984                 break;
21985             case "string":
21986                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21987                 break;
21988             case "int":
21989                 cv = function(v){
21990                     return v !== undefined && v !== null && v !== '' ?
21991                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21992                     };
21993                 break;
21994             case "float":
21995                 cv = function(v){
21996                     return v !== undefined && v !== null && v !== '' ?
21997                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21998                     };
21999                 break;
22000             case "bool":
22001             case "boolean":
22002                 cv = function(v){ return v === true || v === "true" || v == 1; };
22003                 break;
22004             case "date":
22005                 cv = function(v){
22006                     if(!v){
22007                         return '';
22008                     }
22009                     if(v instanceof Date){
22010                         return v;
22011                     }
22012                     if(dateFormat){
22013                         if(dateFormat == "timestamp"){
22014                             return new Date(v*1000);
22015                         }
22016                         return Date.parseDate(v, dateFormat);
22017                     }
22018                     var parsed = Date.parse(v);
22019                     return parsed ? new Date(parsed) : null;
22020                 };
22021              break;
22022             
22023         }
22024         this.convert = cv;
22025     }
22026 };
22027
22028 Roo.data.Field.prototype = {
22029     dateFormat: null,
22030     defaultValue: "",
22031     mapping: null,
22032     sortType : null,
22033     sortDir : "ASC"
22034 };/*
22035  * Based on:
22036  * Ext JS Library 1.1.1
22037  * Copyright(c) 2006-2007, Ext JS, LLC.
22038  *
22039  * Originally Released Under LGPL - original licence link has changed is not relivant.
22040  *
22041  * Fork - LGPL
22042  * <script type="text/javascript">
22043  */
22044  
22045 // Base class for reading structured data from a data source.  This class is intended to be
22046 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22047
22048 /**
22049  * @class Roo.data.DataReader
22050  * Base class for reading structured data from a data source.  This class is intended to be
22051  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22052  */
22053
22054 Roo.data.DataReader = function(meta, recordType){
22055     
22056     this.meta = meta;
22057     
22058     this.recordType = recordType instanceof Array ? 
22059         Roo.data.Record.create(recordType) : recordType;
22060 };
22061
22062 Roo.data.DataReader.prototype = {
22063      /**
22064      * Create an empty record
22065      * @param {Object} data (optional) - overlay some values
22066      * @return {Roo.data.Record} record created.
22067      */
22068     newRow :  function(d) {
22069         var da =  {};
22070         this.recordType.prototype.fields.each(function(c) {
22071             switch( c.type) {
22072                 case 'int' : da[c.name] = 0; break;
22073                 case 'date' : da[c.name] = new Date(); break;
22074                 case 'float' : da[c.name] = 0.0; break;
22075                 case 'boolean' : da[c.name] = false; break;
22076                 default : da[c.name] = ""; break;
22077             }
22078             
22079         });
22080         return new this.recordType(Roo.apply(da, d));
22081     }
22082     
22083 };/*
22084  * Based on:
22085  * Ext JS Library 1.1.1
22086  * Copyright(c) 2006-2007, Ext JS, LLC.
22087  *
22088  * Originally Released Under LGPL - original licence link has changed is not relivant.
22089  *
22090  * Fork - LGPL
22091  * <script type="text/javascript">
22092  */
22093
22094 /**
22095  * @class Roo.data.DataProxy
22096  * @extends Roo.data.Observable
22097  * This class is an abstract base class for implementations which provide retrieval of
22098  * unformatted data objects.<br>
22099  * <p>
22100  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22101  * (of the appropriate type which knows how to parse the data object) to provide a block of
22102  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22103  * <p>
22104  * Custom implementations must implement the load method as described in
22105  * {@link Roo.data.HttpProxy#load}.
22106  */
22107 Roo.data.DataProxy = function(){
22108     this.addEvents({
22109         /**
22110          * @event beforeload
22111          * Fires before a network request is made to retrieve a data object.
22112          * @param {Object} This DataProxy object.
22113          * @param {Object} params The params parameter to the load function.
22114          */
22115         beforeload : true,
22116         /**
22117          * @event load
22118          * Fires before the load method's callback is called.
22119          * @param {Object} This DataProxy object.
22120          * @param {Object} o The data object.
22121          * @param {Object} arg The callback argument object passed to the load function.
22122          */
22123         load : true,
22124         /**
22125          * @event loadexception
22126          * Fires if an Exception occurs during data retrieval.
22127          * @param {Object} This DataProxy object.
22128          * @param {Object} o The data object.
22129          * @param {Object} arg The callback argument object passed to the load function.
22130          * @param {Object} e The Exception.
22131          */
22132         loadexception : true
22133     });
22134     Roo.data.DataProxy.superclass.constructor.call(this);
22135 };
22136
22137 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22138
22139     /**
22140      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22141      */
22142 /*
22143  * Based on:
22144  * Ext JS Library 1.1.1
22145  * Copyright(c) 2006-2007, Ext JS, LLC.
22146  *
22147  * Originally Released Under LGPL - original licence link has changed is not relivant.
22148  *
22149  * Fork - LGPL
22150  * <script type="text/javascript">
22151  */
22152 /**
22153  * @class Roo.data.MemoryProxy
22154  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22155  * to the Reader when its load method is called.
22156  * @constructor
22157  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22158  */
22159 Roo.data.MemoryProxy = function(data){
22160     if (data.data) {
22161         data = data.data;
22162     }
22163     Roo.data.MemoryProxy.superclass.constructor.call(this);
22164     this.data = data;
22165 };
22166
22167 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22168     /**
22169      * Load data from the requested source (in this case an in-memory
22170      * data object passed to the constructor), read the data object into
22171      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22172      * process that block using the passed callback.
22173      * @param {Object} params This parameter is not used by the MemoryProxy class.
22174      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22175      * object into a block of Roo.data.Records.
22176      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22177      * The function must be passed <ul>
22178      * <li>The Record block object</li>
22179      * <li>The "arg" argument from the load function</li>
22180      * <li>A boolean success indicator</li>
22181      * </ul>
22182      * @param {Object} scope The scope in which to call the callback
22183      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22184      */
22185     load : function(params, reader, callback, scope, arg){
22186         params = params || {};
22187         var result;
22188         try {
22189             result = reader.readRecords(this.data);
22190         }catch(e){
22191             this.fireEvent("loadexception", this, arg, null, e);
22192             callback.call(scope, null, arg, false);
22193             return;
22194         }
22195         callback.call(scope, result, arg, true);
22196     },
22197     
22198     // private
22199     update : function(params, records){
22200         
22201     }
22202 });/*
22203  * Based on:
22204  * Ext JS Library 1.1.1
22205  * Copyright(c) 2006-2007, Ext JS, LLC.
22206  *
22207  * Originally Released Under LGPL - original licence link has changed is not relivant.
22208  *
22209  * Fork - LGPL
22210  * <script type="text/javascript">
22211  */
22212 /**
22213  * @class Roo.data.HttpProxy
22214  * @extends Roo.data.DataProxy
22215  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22216  * configured to reference a certain URL.<br><br>
22217  * <p>
22218  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22219  * from which the running page was served.<br><br>
22220  * <p>
22221  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22222  * <p>
22223  * Be aware that to enable the browser to parse an XML document, the server must set
22224  * the Content-Type header in the HTTP response to "text/xml".
22225  * @constructor
22226  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22227  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22228  * will be used to make the request.
22229  */
22230 Roo.data.HttpProxy = function(conn){
22231     Roo.data.HttpProxy.superclass.constructor.call(this);
22232     // is conn a conn config or a real conn?
22233     this.conn = conn;
22234     this.useAjax = !conn || !conn.events;
22235   
22236 };
22237
22238 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22239     // thse are take from connection...
22240     
22241     /**
22242      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22243      */
22244     /**
22245      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22246      * extra parameters to each request made by this object. (defaults to undefined)
22247      */
22248     /**
22249      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22250      *  to each request made by this object. (defaults to undefined)
22251      */
22252     /**
22253      * @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)
22254      */
22255     /**
22256      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22257      */
22258      /**
22259      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22260      * @type Boolean
22261      */
22262   
22263
22264     /**
22265      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22266      * @type Boolean
22267      */
22268     /**
22269      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22270      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22271      * a finer-grained basis than the DataProxy events.
22272      */
22273     getConnection : function(){
22274         return this.useAjax ? Roo.Ajax : this.conn;
22275     },
22276
22277     /**
22278      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22279      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22280      * process that block using the passed callback.
22281      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22282      * for the request to the remote server.
22283      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22284      * object into a block of Roo.data.Records.
22285      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22286      * The function must be passed <ul>
22287      * <li>The Record block object</li>
22288      * <li>The "arg" argument from the load function</li>
22289      * <li>A boolean success indicator</li>
22290      * </ul>
22291      * @param {Object} scope The scope in which to call the callback
22292      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22293      */
22294     load : function(params, reader, callback, scope, arg){
22295         if(this.fireEvent("beforeload", this, params) !== false){
22296             var  o = {
22297                 params : params || {},
22298                 request: {
22299                     callback : callback,
22300                     scope : scope,
22301                     arg : arg
22302                 },
22303                 reader: reader,
22304                 callback : this.loadResponse,
22305                 scope: this
22306             };
22307             if(this.useAjax){
22308                 Roo.applyIf(o, this.conn);
22309                 if(this.activeRequest){
22310                     Roo.Ajax.abort(this.activeRequest);
22311                 }
22312                 this.activeRequest = Roo.Ajax.request(o);
22313             }else{
22314                 this.conn.request(o);
22315             }
22316         }else{
22317             callback.call(scope||this, null, arg, false);
22318         }
22319     },
22320
22321     // private
22322     loadResponse : function(o, success, response){
22323         delete this.activeRequest;
22324         if(!success){
22325             this.fireEvent("loadexception", this, o, response);
22326             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22327             return;
22328         }
22329         var result;
22330         try {
22331             result = o.reader.read(response);
22332         }catch(e){
22333             this.fireEvent("loadexception", this, o, response, e);
22334             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22335             return;
22336         }
22337         
22338         this.fireEvent("load", this, o, o.request.arg);
22339         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22340     },
22341
22342     // private
22343     update : function(dataSet){
22344
22345     },
22346
22347     // private
22348     updateResponse : function(dataSet){
22349
22350     }
22351 });/*
22352  * Based on:
22353  * Ext JS Library 1.1.1
22354  * Copyright(c) 2006-2007, Ext JS, LLC.
22355  *
22356  * Originally Released Under LGPL - original licence link has changed is not relivant.
22357  *
22358  * Fork - LGPL
22359  * <script type="text/javascript">
22360  */
22361
22362 /**
22363  * @class Roo.data.ScriptTagProxy
22364  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22365  * other than the originating domain of the running page.<br><br>
22366  * <p>
22367  * <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
22368  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22369  * <p>
22370  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22371  * source code that is used as the source inside a &lt;script> tag.<br><br>
22372  * <p>
22373  * In order for the browser to process the returned data, the server must wrap the data object
22374  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22375  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22376  * depending on whether the callback name was passed:
22377  * <p>
22378  * <pre><code>
22379 boolean scriptTag = false;
22380 String cb = request.getParameter("callback");
22381 if (cb != null) {
22382     scriptTag = true;
22383     response.setContentType("text/javascript");
22384 } else {
22385     response.setContentType("application/x-json");
22386 }
22387 Writer out = response.getWriter();
22388 if (scriptTag) {
22389     out.write(cb + "(");
22390 }
22391 out.print(dataBlock.toJsonString());
22392 if (scriptTag) {
22393     out.write(");");
22394 }
22395 </pre></code>
22396  *
22397  * @constructor
22398  * @param {Object} config A configuration object.
22399  */
22400 Roo.data.ScriptTagProxy = function(config){
22401     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22402     Roo.apply(this, config);
22403     this.head = document.getElementsByTagName("head")[0];
22404 };
22405
22406 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22407
22408 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22409     /**
22410      * @cfg {String} url The URL from which to request the data object.
22411      */
22412     /**
22413      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22414      */
22415     timeout : 30000,
22416     /**
22417      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22418      * the server the name of the callback function set up by the load call to process the returned data object.
22419      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22420      * javascript output which calls this named function passing the data object as its only parameter.
22421      */
22422     callbackParam : "callback",
22423     /**
22424      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22425      * name to the request.
22426      */
22427     nocache : true,
22428
22429     /**
22430      * Load data from the configured URL, read the data object into
22431      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22432      * process that block using the passed callback.
22433      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22434      * for the request to the remote server.
22435      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22436      * object into a block of Roo.data.Records.
22437      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22438      * The function must be passed <ul>
22439      * <li>The Record block object</li>
22440      * <li>The "arg" argument from the load function</li>
22441      * <li>A boolean success indicator</li>
22442      * </ul>
22443      * @param {Object} scope The scope in which to call the callback
22444      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22445      */
22446     load : function(params, reader, callback, scope, arg){
22447         if(this.fireEvent("beforeload", this, params) !== false){
22448
22449             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22450
22451             var url = this.url;
22452             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22453             if(this.nocache){
22454                 url += "&_dc=" + (new Date().getTime());
22455             }
22456             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22457             var trans = {
22458                 id : transId,
22459                 cb : "stcCallback"+transId,
22460                 scriptId : "stcScript"+transId,
22461                 params : params,
22462                 arg : arg,
22463                 url : url,
22464                 callback : callback,
22465                 scope : scope,
22466                 reader : reader
22467             };
22468             var conn = this;
22469
22470             window[trans.cb] = function(o){
22471                 conn.handleResponse(o, trans);
22472             };
22473
22474             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22475
22476             if(this.autoAbort !== false){
22477                 this.abort();
22478             }
22479
22480             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22481
22482             var script = document.createElement("script");
22483             script.setAttribute("src", url);
22484             script.setAttribute("type", "text/javascript");
22485             script.setAttribute("id", trans.scriptId);
22486             this.head.appendChild(script);
22487
22488             this.trans = trans;
22489         }else{
22490             callback.call(scope||this, null, arg, false);
22491         }
22492     },
22493
22494     // private
22495     isLoading : function(){
22496         return this.trans ? true : false;
22497     },
22498
22499     /**
22500      * Abort the current server request.
22501      */
22502     abort : function(){
22503         if(this.isLoading()){
22504             this.destroyTrans(this.trans);
22505         }
22506     },
22507
22508     // private
22509     destroyTrans : function(trans, isLoaded){
22510         this.head.removeChild(document.getElementById(trans.scriptId));
22511         clearTimeout(trans.timeoutId);
22512         if(isLoaded){
22513             window[trans.cb] = undefined;
22514             try{
22515                 delete window[trans.cb];
22516             }catch(e){}
22517         }else{
22518             // if hasn't been loaded, wait for load to remove it to prevent script error
22519             window[trans.cb] = function(){
22520                 window[trans.cb] = undefined;
22521                 try{
22522                     delete window[trans.cb];
22523                 }catch(e){}
22524             };
22525         }
22526     },
22527
22528     // private
22529     handleResponse : function(o, trans){
22530         this.trans = false;
22531         this.destroyTrans(trans, true);
22532         var result;
22533         try {
22534             result = trans.reader.readRecords(o);
22535         }catch(e){
22536             this.fireEvent("loadexception", this, o, trans.arg, e);
22537             trans.callback.call(trans.scope||window, null, trans.arg, false);
22538             return;
22539         }
22540         this.fireEvent("load", this, o, trans.arg);
22541         trans.callback.call(trans.scope||window, result, trans.arg, true);
22542     },
22543
22544     // private
22545     handleFailure : function(trans){
22546         this.trans = false;
22547         this.destroyTrans(trans, false);
22548         this.fireEvent("loadexception", this, null, trans.arg);
22549         trans.callback.call(trans.scope||window, null, trans.arg, false);
22550     }
22551 });/*
22552  * Based on:
22553  * Ext JS Library 1.1.1
22554  * Copyright(c) 2006-2007, Ext JS, LLC.
22555  *
22556  * Originally Released Under LGPL - original licence link has changed is not relivant.
22557  *
22558  * Fork - LGPL
22559  * <script type="text/javascript">
22560  */
22561
22562 /**
22563  * @class Roo.data.JsonReader
22564  * @extends Roo.data.DataReader
22565  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22566  * based on mappings in a provided Roo.data.Record constructor.
22567  * 
22568  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22569  * in the reply previously. 
22570  * 
22571  * <p>
22572  * Example code:
22573  * <pre><code>
22574 var RecordDef = Roo.data.Record.create([
22575     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22576     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22577 ]);
22578 var myReader = new Roo.data.JsonReader({
22579     totalProperty: "results",    // The property which contains the total dataset size (optional)
22580     root: "rows",                // The property which contains an Array of row objects
22581     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22582 }, RecordDef);
22583 </code></pre>
22584  * <p>
22585  * This would consume a JSON file like this:
22586  * <pre><code>
22587 { 'results': 2, 'rows': [
22588     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22589     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22590 }
22591 </code></pre>
22592  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22593  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22594  * paged from the remote server.
22595  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22596  * @cfg {String} root name of the property which contains the Array of row objects.
22597  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22598  * @constructor
22599  * Create a new JsonReader
22600  * @param {Object} meta Metadata configuration options
22601  * @param {Object} recordType Either an Array of field definition objects,
22602  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22603  */
22604 Roo.data.JsonReader = function(meta, recordType){
22605     
22606     meta = meta || {};
22607     // set some defaults:
22608     Roo.applyIf(meta, {
22609         totalProperty: 'total',
22610         successProperty : 'success',
22611         root : 'data',
22612         id : 'id'
22613     });
22614     
22615     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22616 };
22617 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22618     
22619     /**
22620      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22621      * Used by Store query builder to append _requestMeta to params.
22622      * 
22623      */
22624     metaFromRemote : false,
22625     /**
22626      * This method is only used by a DataProxy which has retrieved data from a remote server.
22627      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22628      * @return {Object} data A data block which is used by an Roo.data.Store object as
22629      * a cache of Roo.data.Records.
22630      */
22631     read : function(response){
22632         var json = response.responseText;
22633        
22634         var o = /* eval:var:o */ eval("("+json+")");
22635         if(!o) {
22636             throw {message: "JsonReader.read: Json object not found"};
22637         }
22638         
22639         if(o.metaData){
22640             
22641             delete this.ef;
22642             this.metaFromRemote = true;
22643             this.meta = o.metaData;
22644             this.recordType = Roo.data.Record.create(o.metaData.fields);
22645             this.onMetaChange(this.meta, this.recordType, o);
22646         }
22647         return this.readRecords(o);
22648     },
22649
22650     // private function a store will implement
22651     onMetaChange : function(meta, recordType, o){
22652
22653     },
22654
22655     /**
22656          * @ignore
22657          */
22658     simpleAccess: function(obj, subsc) {
22659         return obj[subsc];
22660     },
22661
22662         /**
22663          * @ignore
22664          */
22665     getJsonAccessor: function(){
22666         var re = /[\[\.]/;
22667         return function(expr) {
22668             try {
22669                 return(re.test(expr))
22670                     ? new Function("obj", "return obj." + expr)
22671                     : function(obj){
22672                         return obj[expr];
22673                     };
22674             } catch(e){}
22675             return Roo.emptyFn;
22676         };
22677     }(),
22678
22679     /**
22680      * Create a data block containing Roo.data.Records from an XML document.
22681      * @param {Object} o An object which contains an Array of row objects in the property specified
22682      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22683      * which contains the total size of the dataset.
22684      * @return {Object} data A data block which is used by an Roo.data.Store object as
22685      * a cache of Roo.data.Records.
22686      */
22687     readRecords : function(o){
22688         /**
22689          * After any data loads, the raw JSON data is available for further custom processing.
22690          * @type Object
22691          */
22692         this.o = o;
22693         var s = this.meta, Record = this.recordType,
22694             f = Record.prototype.fields, fi = f.items, fl = f.length;
22695
22696 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22697         if (!this.ef) {
22698             if(s.totalProperty) {
22699                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22700                 }
22701                 if(s.successProperty) {
22702                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22703                 }
22704                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22705                 if (s.id) {
22706                         var g = this.getJsonAccessor(s.id);
22707                         this.getId = function(rec) {
22708                                 var r = g(rec);  
22709                                 return (r === undefined || r === "") ? null : r;
22710                         };
22711                 } else {
22712                         this.getId = function(){return null;};
22713                 }
22714             this.ef = [];
22715             for(var jj = 0; jj < fl; jj++){
22716                 f = fi[jj];
22717                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22718                 this.ef[jj] = this.getJsonAccessor(map);
22719             }
22720         }
22721
22722         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22723         if(s.totalProperty){
22724             var vt = parseInt(this.getTotal(o), 10);
22725             if(!isNaN(vt)){
22726                 totalRecords = vt;
22727             }
22728         }
22729         if(s.successProperty){
22730             var vs = this.getSuccess(o);
22731             if(vs === false || vs === 'false'){
22732                 success = false;
22733             }
22734         }
22735         var records = [];
22736             for(var i = 0; i < c; i++){
22737                     var n = root[i];
22738                 var values = {};
22739                 var id = this.getId(n);
22740                 for(var j = 0; j < fl; j++){
22741                     f = fi[j];
22742                 var v = this.ef[j](n);
22743                 if (!f.convert) {
22744                     Roo.log('missing convert for ' + f.name);
22745                     Roo.log(f);
22746                     continue;
22747                 }
22748                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22749                 }
22750                 var record = new Record(values, id);
22751                 record.json = n;
22752                 records[i] = record;
22753             }
22754             return {
22755             raw : o,
22756                 success : success,
22757                 records : records,
22758                 totalRecords : totalRecords
22759             };
22760     }
22761 });/*
22762  * Based on:
22763  * Ext JS Library 1.1.1
22764  * Copyright(c) 2006-2007, Ext JS, LLC.
22765  *
22766  * Originally Released Under LGPL - original licence link has changed is not relivant.
22767  *
22768  * Fork - LGPL
22769  * <script type="text/javascript">
22770  */
22771
22772 /**
22773  * @class Roo.data.XmlReader
22774  * @extends Roo.data.DataReader
22775  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22776  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22777  * <p>
22778  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22779  * header in the HTTP response must be set to "text/xml".</em>
22780  * <p>
22781  * Example code:
22782  * <pre><code>
22783 var RecordDef = Roo.data.Record.create([
22784    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22785    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22786 ]);
22787 var myReader = new Roo.data.XmlReader({
22788    totalRecords: "results", // The element which contains the total dataset size (optional)
22789    record: "row",           // The repeated element which contains row information
22790    id: "id"                 // The element within the row that provides an ID for the record (optional)
22791 }, RecordDef);
22792 </code></pre>
22793  * <p>
22794  * This would consume an XML file like this:
22795  * <pre><code>
22796 &lt;?xml?>
22797 &lt;dataset>
22798  &lt;results>2&lt;/results>
22799  &lt;row>
22800    &lt;id>1&lt;/id>
22801    &lt;name>Bill&lt;/name>
22802    &lt;occupation>Gardener&lt;/occupation>
22803  &lt;/row>
22804  &lt;row>
22805    &lt;id>2&lt;/id>
22806    &lt;name>Ben&lt;/name>
22807    &lt;occupation>Horticulturalist&lt;/occupation>
22808  &lt;/row>
22809 &lt;/dataset>
22810 </code></pre>
22811  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22812  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22813  * paged from the remote server.
22814  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22815  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22816  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22817  * a record identifier value.
22818  * @constructor
22819  * Create a new XmlReader
22820  * @param {Object} meta Metadata configuration options
22821  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22822  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22823  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22824  */
22825 Roo.data.XmlReader = function(meta, recordType){
22826     meta = meta || {};
22827     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22828 };
22829 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22830     /**
22831      * This method is only used by a DataProxy which has retrieved data from a remote server.
22832          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22833          * to contain a method called 'responseXML' that returns an XML document object.
22834      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22835      * a cache of Roo.data.Records.
22836      */
22837     read : function(response){
22838         var doc = response.responseXML;
22839         if(!doc) {
22840             throw {message: "XmlReader.read: XML Document not available"};
22841         }
22842         return this.readRecords(doc);
22843     },
22844
22845     /**
22846      * Create a data block containing Roo.data.Records from an XML document.
22847          * @param {Object} doc A parsed XML document.
22848      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22849      * a cache of Roo.data.Records.
22850      */
22851     readRecords : function(doc){
22852         /**
22853          * After any data loads/reads, the raw XML Document is available for further custom processing.
22854          * @type XMLDocument
22855          */
22856         this.xmlData = doc;
22857         var root = doc.documentElement || doc;
22858         var q = Roo.DomQuery;
22859         var recordType = this.recordType, fields = recordType.prototype.fields;
22860         var sid = this.meta.id;
22861         var totalRecords = 0, success = true;
22862         if(this.meta.totalRecords){
22863             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22864         }
22865         
22866         if(this.meta.success){
22867             var sv = q.selectValue(this.meta.success, root, true);
22868             success = sv !== false && sv !== 'false';
22869         }
22870         var records = [];
22871         var ns = q.select(this.meta.record, root);
22872         for(var i = 0, len = ns.length; i < len; i++) {
22873                 var n = ns[i];
22874                 var values = {};
22875                 var id = sid ? q.selectValue(sid, n) : undefined;
22876                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22877                     var f = fields.items[j];
22878                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22879                     v = f.convert(v);
22880                     values[f.name] = v;
22881                 }
22882                 var record = new recordType(values, id);
22883                 record.node = n;
22884                 records[records.length] = record;
22885             }
22886
22887             return {
22888                 success : success,
22889                 records : records,
22890                 totalRecords : totalRecords || records.length
22891             };
22892     }
22893 });/*
22894  * Based on:
22895  * Ext JS Library 1.1.1
22896  * Copyright(c) 2006-2007, Ext JS, LLC.
22897  *
22898  * Originally Released Under LGPL - original licence link has changed is not relivant.
22899  *
22900  * Fork - LGPL
22901  * <script type="text/javascript">
22902  */
22903
22904 /**
22905  * @class Roo.data.ArrayReader
22906  * @extends Roo.data.DataReader
22907  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22908  * Each element of that Array represents a row of data fields. The
22909  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22910  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22911  * <p>
22912  * Example code:.
22913  * <pre><code>
22914 var RecordDef = Roo.data.Record.create([
22915     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22916     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22917 ]);
22918 var myReader = new Roo.data.ArrayReader({
22919     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22920 }, RecordDef);
22921 </code></pre>
22922  * <p>
22923  * This would consume an Array like this:
22924  * <pre><code>
22925 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22926   </code></pre>
22927  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22928  * @constructor
22929  * Create a new JsonReader
22930  * @param {Object} meta Metadata configuration options.
22931  * @param {Object} recordType Either an Array of field definition objects
22932  * as specified to {@link Roo.data.Record#create},
22933  * or an {@link Roo.data.Record} object
22934  * created using {@link Roo.data.Record#create}.
22935  */
22936 Roo.data.ArrayReader = function(meta, recordType){
22937     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22938 };
22939
22940 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22941     /**
22942      * Create a data block containing Roo.data.Records from an XML document.
22943      * @param {Object} o An Array of row objects which represents the dataset.
22944      * @return {Object} data A data block which is used by an Roo.data.Store object as
22945      * a cache of Roo.data.Records.
22946      */
22947     readRecords : function(o){
22948         var sid = this.meta ? this.meta.id : null;
22949         var recordType = this.recordType, fields = recordType.prototype.fields;
22950         var records = [];
22951         var root = o;
22952             for(var i = 0; i < root.length; i++){
22953                     var n = root[i];
22954                 var values = {};
22955                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22956                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22957                 var f = fields.items[j];
22958                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22959                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22960                 v = f.convert(v);
22961                 values[f.name] = v;
22962             }
22963                 var record = new recordType(values, id);
22964                 record.json = n;
22965                 records[records.length] = record;
22966             }
22967             return {
22968                 records : records,
22969                 totalRecords : records.length
22970             };
22971     }
22972 });/*
22973  * Based on:
22974  * Ext JS Library 1.1.1
22975  * Copyright(c) 2006-2007, Ext JS, LLC.
22976  *
22977  * Originally Released Under LGPL - original licence link has changed is not relivant.
22978  *
22979  * Fork - LGPL
22980  * <script type="text/javascript">
22981  */
22982
22983
22984 /**
22985  * @class Roo.data.Tree
22986  * @extends Roo.util.Observable
22987  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22988  * in the tree have most standard DOM functionality.
22989  * @constructor
22990  * @param {Node} root (optional) The root node
22991  */
22992 Roo.data.Tree = function(root){
22993    this.nodeHash = {};
22994    /**
22995     * The root node for this tree
22996     * @type Node
22997     */
22998    this.root = null;
22999    if(root){
23000        this.setRootNode(root);
23001    }
23002    this.addEvents({
23003        /**
23004         * @event append
23005         * Fires when a new child node is appended to a node in this tree.
23006         * @param {Tree} tree The owner tree
23007         * @param {Node} parent The parent node
23008         * @param {Node} node The newly appended node
23009         * @param {Number} index The index of the newly appended node
23010         */
23011        "append" : true,
23012        /**
23013         * @event remove
23014         * Fires when a child node is removed from a node in this tree.
23015         * @param {Tree} tree The owner tree
23016         * @param {Node} parent The parent node
23017         * @param {Node} node The child node removed
23018         */
23019        "remove" : true,
23020        /**
23021         * @event move
23022         * Fires when a node is moved to a new location in the tree
23023         * @param {Tree} tree The owner tree
23024         * @param {Node} node The node moved
23025         * @param {Node} oldParent The old parent of this node
23026         * @param {Node} newParent The new parent of this node
23027         * @param {Number} index The index it was moved to
23028         */
23029        "move" : true,
23030        /**
23031         * @event insert
23032         * Fires when a new child node is inserted in a node in this tree.
23033         * @param {Tree} tree The owner tree
23034         * @param {Node} parent The parent node
23035         * @param {Node} node The child node inserted
23036         * @param {Node} refNode The child node the node was inserted before
23037         */
23038        "insert" : true,
23039        /**
23040         * @event beforeappend
23041         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23042         * @param {Tree} tree The owner tree
23043         * @param {Node} parent The parent node
23044         * @param {Node} node The child node to be appended
23045         */
23046        "beforeappend" : true,
23047        /**
23048         * @event beforeremove
23049         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23050         * @param {Tree} tree The owner tree
23051         * @param {Node} parent The parent node
23052         * @param {Node} node The child node to be removed
23053         */
23054        "beforeremove" : true,
23055        /**
23056         * @event beforemove
23057         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23058         * @param {Tree} tree The owner tree
23059         * @param {Node} node The node being moved
23060         * @param {Node} oldParent The parent of the node
23061         * @param {Node} newParent The new parent the node is moving to
23062         * @param {Number} index The index it is being moved to
23063         */
23064        "beforemove" : true,
23065        /**
23066         * @event beforeinsert
23067         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23068         * @param {Tree} tree The owner tree
23069         * @param {Node} parent The parent node
23070         * @param {Node} node The child node to be inserted
23071         * @param {Node} refNode The child node the node is being inserted before
23072         */
23073        "beforeinsert" : true
23074    });
23075
23076     Roo.data.Tree.superclass.constructor.call(this);
23077 };
23078
23079 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23080     pathSeparator: "/",
23081
23082     proxyNodeEvent : function(){
23083         return this.fireEvent.apply(this, arguments);
23084     },
23085
23086     /**
23087      * Returns the root node for this tree.
23088      * @return {Node}
23089      */
23090     getRootNode : function(){
23091         return this.root;
23092     },
23093
23094     /**
23095      * Sets the root node for this tree.
23096      * @param {Node} node
23097      * @return {Node}
23098      */
23099     setRootNode : function(node){
23100         this.root = node;
23101         node.ownerTree = this;
23102         node.isRoot = true;
23103         this.registerNode(node);
23104         return node;
23105     },
23106
23107     /**
23108      * Gets a node in this tree by its id.
23109      * @param {String} id
23110      * @return {Node}
23111      */
23112     getNodeById : function(id){
23113         return this.nodeHash[id];
23114     },
23115
23116     registerNode : function(node){
23117         this.nodeHash[node.id] = node;
23118     },
23119
23120     unregisterNode : function(node){
23121         delete this.nodeHash[node.id];
23122     },
23123
23124     toString : function(){
23125         return "[Tree"+(this.id?" "+this.id:"")+"]";
23126     }
23127 });
23128
23129 /**
23130  * @class Roo.data.Node
23131  * @extends Roo.util.Observable
23132  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23133  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23134  * @constructor
23135  * @param {Object} attributes The attributes/config for the node
23136  */
23137 Roo.data.Node = function(attributes){
23138     /**
23139      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23140      * @type {Object}
23141      */
23142     this.attributes = attributes || {};
23143     this.leaf = this.attributes.leaf;
23144     /**
23145      * The node id. @type String
23146      */
23147     this.id = this.attributes.id;
23148     if(!this.id){
23149         this.id = Roo.id(null, "ynode-");
23150         this.attributes.id = this.id;
23151     }
23152      
23153     
23154     /**
23155      * All child nodes of this node. @type Array
23156      */
23157     this.childNodes = [];
23158     if(!this.childNodes.indexOf){ // indexOf is a must
23159         this.childNodes.indexOf = function(o){
23160             for(var i = 0, len = this.length; i < len; i++){
23161                 if(this[i] == o) {
23162                     return i;
23163                 }
23164             }
23165             return -1;
23166         };
23167     }
23168     /**
23169      * The parent node for this node. @type Node
23170      */
23171     this.parentNode = null;
23172     /**
23173      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23174      */
23175     this.firstChild = null;
23176     /**
23177      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23178      */
23179     this.lastChild = null;
23180     /**
23181      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23182      */
23183     this.previousSibling = null;
23184     /**
23185      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23186      */
23187     this.nextSibling = null;
23188
23189     this.addEvents({
23190        /**
23191         * @event append
23192         * Fires when a new child node is appended
23193         * @param {Tree} tree The owner tree
23194         * @param {Node} this This node
23195         * @param {Node} node The newly appended node
23196         * @param {Number} index The index of the newly appended node
23197         */
23198        "append" : true,
23199        /**
23200         * @event remove
23201         * Fires when a child node is removed
23202         * @param {Tree} tree The owner tree
23203         * @param {Node} this This node
23204         * @param {Node} node The removed node
23205         */
23206        "remove" : true,
23207        /**
23208         * @event move
23209         * Fires when this node is moved to a new location in the tree
23210         * @param {Tree} tree The owner tree
23211         * @param {Node} this This node
23212         * @param {Node} oldParent The old parent of this node
23213         * @param {Node} newParent The new parent of this node
23214         * @param {Number} index The index it was moved to
23215         */
23216        "move" : true,
23217        /**
23218         * @event insert
23219         * Fires when a new child node is inserted.
23220         * @param {Tree} tree The owner tree
23221         * @param {Node} this This node
23222         * @param {Node} node The child node inserted
23223         * @param {Node} refNode The child node the node was inserted before
23224         */
23225        "insert" : true,
23226        /**
23227         * @event beforeappend
23228         * Fires before a new child is appended, return false to cancel the append.
23229         * @param {Tree} tree The owner tree
23230         * @param {Node} this This node
23231         * @param {Node} node The child node to be appended
23232         */
23233        "beforeappend" : true,
23234        /**
23235         * @event beforeremove
23236         * Fires before a child is removed, return false to cancel the remove.
23237         * @param {Tree} tree The owner tree
23238         * @param {Node} this This node
23239         * @param {Node} node The child node to be removed
23240         */
23241        "beforeremove" : true,
23242        /**
23243         * @event beforemove
23244         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23245         * @param {Tree} tree The owner tree
23246         * @param {Node} this This node
23247         * @param {Node} oldParent The parent of this node
23248         * @param {Node} newParent The new parent this node is moving to
23249         * @param {Number} index The index it is being moved to
23250         */
23251        "beforemove" : true,
23252        /**
23253         * @event beforeinsert
23254         * Fires before a new child is inserted, return false to cancel the insert.
23255         * @param {Tree} tree The owner tree
23256         * @param {Node} this This node
23257         * @param {Node} node The child node to be inserted
23258         * @param {Node} refNode The child node the node is being inserted before
23259         */
23260        "beforeinsert" : true
23261    });
23262     this.listeners = this.attributes.listeners;
23263     Roo.data.Node.superclass.constructor.call(this);
23264 };
23265
23266 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23267     fireEvent : function(evtName){
23268         // first do standard event for this node
23269         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23270             return false;
23271         }
23272         // then bubble it up to the tree if the event wasn't cancelled
23273         var ot = this.getOwnerTree();
23274         if(ot){
23275             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23276                 return false;
23277             }
23278         }
23279         return true;
23280     },
23281
23282     /**
23283      * Returns true if this node is a leaf
23284      * @return {Boolean}
23285      */
23286     isLeaf : function(){
23287         return this.leaf === true;
23288     },
23289
23290     // private
23291     setFirstChild : function(node){
23292         this.firstChild = node;
23293     },
23294
23295     //private
23296     setLastChild : function(node){
23297         this.lastChild = node;
23298     },
23299
23300
23301     /**
23302      * Returns true if this node is the last child of its parent
23303      * @return {Boolean}
23304      */
23305     isLast : function(){
23306        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23307     },
23308
23309     /**
23310      * Returns true if this node is the first child of its parent
23311      * @return {Boolean}
23312      */
23313     isFirst : function(){
23314        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23315     },
23316
23317     hasChildNodes : function(){
23318         return !this.isLeaf() && this.childNodes.length > 0;
23319     },
23320
23321     /**
23322      * Insert node(s) as the last child node of this node.
23323      * @param {Node/Array} node The node or Array of nodes to append
23324      * @return {Node} The appended node if single append, or null if an array was passed
23325      */
23326     appendChild : function(node){
23327         var multi = false;
23328         if(node instanceof Array){
23329             multi = node;
23330         }else if(arguments.length > 1){
23331             multi = arguments;
23332         }
23333         // if passed an array or multiple args do them one by one
23334         if(multi){
23335             for(var i = 0, len = multi.length; i < len; i++) {
23336                 this.appendChild(multi[i]);
23337             }
23338         }else{
23339             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23340                 return false;
23341             }
23342             var index = this.childNodes.length;
23343             var oldParent = node.parentNode;
23344             // it's a move, make sure we move it cleanly
23345             if(oldParent){
23346                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23347                     return false;
23348                 }
23349                 oldParent.removeChild(node);
23350             }
23351             index = this.childNodes.length;
23352             if(index == 0){
23353                 this.setFirstChild(node);
23354             }
23355             this.childNodes.push(node);
23356             node.parentNode = this;
23357             var ps = this.childNodes[index-1];
23358             if(ps){
23359                 node.previousSibling = ps;
23360                 ps.nextSibling = node;
23361             }else{
23362                 node.previousSibling = null;
23363             }
23364             node.nextSibling = null;
23365             this.setLastChild(node);
23366             node.setOwnerTree(this.getOwnerTree());
23367             this.fireEvent("append", this.ownerTree, this, node, index);
23368             if(oldParent){
23369                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23370             }
23371             return node;
23372         }
23373     },
23374
23375     /**
23376      * Removes a child node from this node.
23377      * @param {Node} node The node to remove
23378      * @return {Node} The removed node
23379      */
23380     removeChild : function(node){
23381         var index = this.childNodes.indexOf(node);
23382         if(index == -1){
23383             return false;
23384         }
23385         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23386             return false;
23387         }
23388
23389         // remove it from childNodes collection
23390         this.childNodes.splice(index, 1);
23391
23392         // update siblings
23393         if(node.previousSibling){
23394             node.previousSibling.nextSibling = node.nextSibling;
23395         }
23396         if(node.nextSibling){
23397             node.nextSibling.previousSibling = node.previousSibling;
23398         }
23399
23400         // update child refs
23401         if(this.firstChild == node){
23402             this.setFirstChild(node.nextSibling);
23403         }
23404         if(this.lastChild == node){
23405             this.setLastChild(node.previousSibling);
23406         }
23407
23408         node.setOwnerTree(null);
23409         // clear any references from the node
23410         node.parentNode = null;
23411         node.previousSibling = null;
23412         node.nextSibling = null;
23413         this.fireEvent("remove", this.ownerTree, this, node);
23414         return node;
23415     },
23416
23417     /**
23418      * Inserts the first node before the second node in this nodes childNodes collection.
23419      * @param {Node} node The node to insert
23420      * @param {Node} refNode The node to insert before (if null the node is appended)
23421      * @return {Node} The inserted node
23422      */
23423     insertBefore : function(node, refNode){
23424         if(!refNode){ // like standard Dom, refNode can be null for append
23425             return this.appendChild(node);
23426         }
23427         // nothing to do
23428         if(node == refNode){
23429             return false;
23430         }
23431
23432         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23433             return false;
23434         }
23435         var index = this.childNodes.indexOf(refNode);
23436         var oldParent = node.parentNode;
23437         var refIndex = index;
23438
23439         // when moving internally, indexes will change after remove
23440         if(oldParent == this && this.childNodes.indexOf(node) < index){
23441             refIndex--;
23442         }
23443
23444         // it's a move, make sure we move it cleanly
23445         if(oldParent){
23446             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23447                 return false;
23448             }
23449             oldParent.removeChild(node);
23450         }
23451         if(refIndex == 0){
23452             this.setFirstChild(node);
23453         }
23454         this.childNodes.splice(refIndex, 0, node);
23455         node.parentNode = this;
23456         var ps = this.childNodes[refIndex-1];
23457         if(ps){
23458             node.previousSibling = ps;
23459             ps.nextSibling = node;
23460         }else{
23461             node.previousSibling = null;
23462         }
23463         node.nextSibling = refNode;
23464         refNode.previousSibling = node;
23465         node.setOwnerTree(this.getOwnerTree());
23466         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23467         if(oldParent){
23468             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23469         }
23470         return node;
23471     },
23472
23473     /**
23474      * Returns the child node at the specified index.
23475      * @param {Number} index
23476      * @return {Node}
23477      */
23478     item : function(index){
23479         return this.childNodes[index];
23480     },
23481
23482     /**
23483      * Replaces one child node in this node with another.
23484      * @param {Node} newChild The replacement node
23485      * @param {Node} oldChild The node to replace
23486      * @return {Node} The replaced node
23487      */
23488     replaceChild : function(newChild, oldChild){
23489         this.insertBefore(newChild, oldChild);
23490         this.removeChild(oldChild);
23491         return oldChild;
23492     },
23493
23494     /**
23495      * Returns the index of a child node
23496      * @param {Node} node
23497      * @return {Number} The index of the node or -1 if it was not found
23498      */
23499     indexOf : function(child){
23500         return this.childNodes.indexOf(child);
23501     },
23502
23503     /**
23504      * Returns the tree this node is in.
23505      * @return {Tree}
23506      */
23507     getOwnerTree : function(){
23508         // if it doesn't have one, look for one
23509         if(!this.ownerTree){
23510             var p = this;
23511             while(p){
23512                 if(p.ownerTree){
23513                     this.ownerTree = p.ownerTree;
23514                     break;
23515                 }
23516                 p = p.parentNode;
23517             }
23518         }
23519         return this.ownerTree;
23520     },
23521
23522     /**
23523      * Returns depth of this node (the root node has a depth of 0)
23524      * @return {Number}
23525      */
23526     getDepth : function(){
23527         var depth = 0;
23528         var p = this;
23529         while(p.parentNode){
23530             ++depth;
23531             p = p.parentNode;
23532         }
23533         return depth;
23534     },
23535
23536     // private
23537     setOwnerTree : function(tree){
23538         // if it's move, we need to update everyone
23539         if(tree != this.ownerTree){
23540             if(this.ownerTree){
23541                 this.ownerTree.unregisterNode(this);
23542             }
23543             this.ownerTree = tree;
23544             var cs = this.childNodes;
23545             for(var i = 0, len = cs.length; i < len; i++) {
23546                 cs[i].setOwnerTree(tree);
23547             }
23548             if(tree){
23549                 tree.registerNode(this);
23550             }
23551         }
23552     },
23553
23554     /**
23555      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23556      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23557      * @return {String} The path
23558      */
23559     getPath : function(attr){
23560         attr = attr || "id";
23561         var p = this.parentNode;
23562         var b = [this.attributes[attr]];
23563         while(p){
23564             b.unshift(p.attributes[attr]);
23565             p = p.parentNode;
23566         }
23567         var sep = this.getOwnerTree().pathSeparator;
23568         return sep + b.join(sep);
23569     },
23570
23571     /**
23572      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23573      * function call will be the scope provided or the current node. The arguments to the function
23574      * will be the args provided or the current node. If the function returns false at any point,
23575      * the bubble is stopped.
23576      * @param {Function} fn The function to call
23577      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23578      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23579      */
23580     bubble : function(fn, scope, args){
23581         var p = this;
23582         while(p){
23583             if(fn.call(scope || p, args || p) === false){
23584                 break;
23585             }
23586             p = p.parentNode;
23587         }
23588     },
23589
23590     /**
23591      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23592      * function call will be the scope provided or the current node. The arguments to the function
23593      * will be the args provided or the current node. If the function returns false at any point,
23594      * the cascade is stopped on that branch.
23595      * @param {Function} fn The function to call
23596      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23597      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23598      */
23599     cascade : function(fn, scope, args){
23600         if(fn.call(scope || this, args || this) !== false){
23601             var cs = this.childNodes;
23602             for(var i = 0, len = cs.length; i < len; i++) {
23603                 cs[i].cascade(fn, scope, args);
23604             }
23605         }
23606     },
23607
23608     /**
23609      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23610      * function call will be the scope provided or the current node. The arguments to the function
23611      * will be the args provided or the current node. If the function returns false at any point,
23612      * the iteration stops.
23613      * @param {Function} fn The function to call
23614      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23615      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23616      */
23617     eachChild : function(fn, scope, args){
23618         var cs = this.childNodes;
23619         for(var i = 0, len = cs.length; i < len; i++) {
23620                 if(fn.call(scope || this, args || cs[i]) === false){
23621                     break;
23622                 }
23623         }
23624     },
23625
23626     /**
23627      * Finds the first child that has the attribute with the specified value.
23628      * @param {String} attribute The attribute name
23629      * @param {Mixed} value The value to search for
23630      * @return {Node} The found child or null if none was found
23631      */
23632     findChild : function(attribute, value){
23633         var cs = this.childNodes;
23634         for(var i = 0, len = cs.length; i < len; i++) {
23635                 if(cs[i].attributes[attribute] == value){
23636                     return cs[i];
23637                 }
23638         }
23639         return null;
23640     },
23641
23642     /**
23643      * Finds the first child by a custom function. The child matches if the function passed
23644      * returns true.
23645      * @param {Function} fn
23646      * @param {Object} scope (optional)
23647      * @return {Node} The found child or null if none was found
23648      */
23649     findChildBy : function(fn, scope){
23650         var cs = this.childNodes;
23651         for(var i = 0, len = cs.length; i < len; i++) {
23652                 if(fn.call(scope||cs[i], cs[i]) === true){
23653                     return cs[i];
23654                 }
23655         }
23656         return null;
23657     },
23658
23659     /**
23660      * Sorts this nodes children using the supplied sort function
23661      * @param {Function} fn
23662      * @param {Object} scope (optional)
23663      */
23664     sort : function(fn, scope){
23665         var cs = this.childNodes;
23666         var len = cs.length;
23667         if(len > 0){
23668             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23669             cs.sort(sortFn);
23670             for(var i = 0; i < len; i++){
23671                 var n = cs[i];
23672                 n.previousSibling = cs[i-1];
23673                 n.nextSibling = cs[i+1];
23674                 if(i == 0){
23675                     this.setFirstChild(n);
23676                 }
23677                 if(i == len-1){
23678                     this.setLastChild(n);
23679                 }
23680             }
23681         }
23682     },
23683
23684     /**
23685      * Returns true if this node is an ancestor (at any point) of the passed node.
23686      * @param {Node} node
23687      * @return {Boolean}
23688      */
23689     contains : function(node){
23690         return node.isAncestor(this);
23691     },
23692
23693     /**
23694      * Returns true if the passed node is an ancestor (at any point) of this node.
23695      * @param {Node} node
23696      * @return {Boolean}
23697      */
23698     isAncestor : function(node){
23699         var p = this.parentNode;
23700         while(p){
23701             if(p == node){
23702                 return true;
23703             }
23704             p = p.parentNode;
23705         }
23706         return false;
23707     },
23708
23709     toString : function(){
23710         return "[Node"+(this.id?" "+this.id:"")+"]";
23711     }
23712 });/*
23713  * Based on:
23714  * Ext JS Library 1.1.1
23715  * Copyright(c) 2006-2007, Ext JS, LLC.
23716  *
23717  * Originally Released Under LGPL - original licence link has changed is not relivant.
23718  *
23719  * Fork - LGPL
23720  * <script type="text/javascript">
23721  */
23722  (function(){ 
23723 /**
23724  * @class Roo.Layer
23725  * @extends Roo.Element
23726  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23727  * automatic maintaining of shadow/shim positions.
23728  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23729  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23730  * you can pass a string with a CSS class name. False turns off the shadow.
23731  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23732  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23733  * @cfg {String} cls CSS class to add to the element
23734  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23735  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23736  * @constructor
23737  * @param {Object} config An object with config options.
23738  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23739  */
23740
23741 Roo.Layer = function(config, existingEl){
23742     config = config || {};
23743     var dh = Roo.DomHelper;
23744     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23745     if(existingEl){
23746         this.dom = Roo.getDom(existingEl);
23747     }
23748     if(!this.dom){
23749         var o = config.dh || {tag: "div", cls: "x-layer"};
23750         this.dom = dh.append(pel, o);
23751     }
23752     if(config.cls){
23753         this.addClass(config.cls);
23754     }
23755     this.constrain = config.constrain !== false;
23756     this.visibilityMode = Roo.Element.VISIBILITY;
23757     if(config.id){
23758         this.id = this.dom.id = config.id;
23759     }else{
23760         this.id = Roo.id(this.dom);
23761     }
23762     this.zindex = config.zindex || this.getZIndex();
23763     this.position("absolute", this.zindex);
23764     if(config.shadow){
23765         this.shadowOffset = config.shadowOffset || 4;
23766         this.shadow = new Roo.Shadow({
23767             offset : this.shadowOffset,
23768             mode : config.shadow
23769         });
23770     }else{
23771         this.shadowOffset = 0;
23772     }
23773     this.useShim = config.shim !== false && Roo.useShims;
23774     this.useDisplay = config.useDisplay;
23775     this.hide();
23776 };
23777
23778 var supr = Roo.Element.prototype;
23779
23780 // shims are shared among layer to keep from having 100 iframes
23781 var shims = [];
23782
23783 Roo.extend(Roo.Layer, Roo.Element, {
23784
23785     getZIndex : function(){
23786         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23787     },
23788
23789     getShim : function(){
23790         if(!this.useShim){
23791             return null;
23792         }
23793         if(this.shim){
23794             return this.shim;
23795         }
23796         var shim = shims.shift();
23797         if(!shim){
23798             shim = this.createShim();
23799             shim.enableDisplayMode('block');
23800             shim.dom.style.display = 'none';
23801             shim.dom.style.visibility = 'visible';
23802         }
23803         var pn = this.dom.parentNode;
23804         if(shim.dom.parentNode != pn){
23805             pn.insertBefore(shim.dom, this.dom);
23806         }
23807         shim.setStyle('z-index', this.getZIndex()-2);
23808         this.shim = shim;
23809         return shim;
23810     },
23811
23812     hideShim : function(){
23813         if(this.shim){
23814             this.shim.setDisplayed(false);
23815             shims.push(this.shim);
23816             delete this.shim;
23817         }
23818     },
23819
23820     disableShadow : function(){
23821         if(this.shadow){
23822             this.shadowDisabled = true;
23823             this.shadow.hide();
23824             this.lastShadowOffset = this.shadowOffset;
23825             this.shadowOffset = 0;
23826         }
23827     },
23828
23829     enableShadow : function(show){
23830         if(this.shadow){
23831             this.shadowDisabled = false;
23832             this.shadowOffset = this.lastShadowOffset;
23833             delete this.lastShadowOffset;
23834             if(show){
23835                 this.sync(true);
23836             }
23837         }
23838     },
23839
23840     // private
23841     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23842     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23843     sync : function(doShow){
23844         var sw = this.shadow;
23845         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23846             var sh = this.getShim();
23847
23848             var w = this.getWidth(),
23849                 h = this.getHeight();
23850
23851             var l = this.getLeft(true),
23852                 t = this.getTop(true);
23853
23854             if(sw && !this.shadowDisabled){
23855                 if(doShow && !sw.isVisible()){
23856                     sw.show(this);
23857                 }else{
23858                     sw.realign(l, t, w, h);
23859                 }
23860                 if(sh){
23861                     if(doShow){
23862                        sh.show();
23863                     }
23864                     // fit the shim behind the shadow, so it is shimmed too
23865                     var a = sw.adjusts, s = sh.dom.style;
23866                     s.left = (Math.min(l, l+a.l))+"px";
23867                     s.top = (Math.min(t, t+a.t))+"px";
23868                     s.width = (w+a.w)+"px";
23869                     s.height = (h+a.h)+"px";
23870                 }
23871             }else if(sh){
23872                 if(doShow){
23873                    sh.show();
23874                 }
23875                 sh.setSize(w, h);
23876                 sh.setLeftTop(l, t);
23877             }
23878             
23879         }
23880     },
23881
23882     // private
23883     destroy : function(){
23884         this.hideShim();
23885         if(this.shadow){
23886             this.shadow.hide();
23887         }
23888         this.removeAllListeners();
23889         var pn = this.dom.parentNode;
23890         if(pn){
23891             pn.removeChild(this.dom);
23892         }
23893         Roo.Element.uncache(this.id);
23894     },
23895
23896     remove : function(){
23897         this.destroy();
23898     },
23899
23900     // private
23901     beginUpdate : function(){
23902         this.updating = true;
23903     },
23904
23905     // private
23906     endUpdate : function(){
23907         this.updating = false;
23908         this.sync(true);
23909     },
23910
23911     // private
23912     hideUnders : function(negOffset){
23913         if(this.shadow){
23914             this.shadow.hide();
23915         }
23916         this.hideShim();
23917     },
23918
23919     // private
23920     constrainXY : function(){
23921         if(this.constrain){
23922             var vw = Roo.lib.Dom.getViewWidth(),
23923                 vh = Roo.lib.Dom.getViewHeight();
23924             var s = Roo.get(document).getScroll();
23925
23926             var xy = this.getXY();
23927             var x = xy[0], y = xy[1];   
23928             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23929             // only move it if it needs it
23930             var moved = false;
23931             // first validate right/bottom
23932             if((x + w) > vw+s.left){
23933                 x = vw - w - this.shadowOffset;
23934                 moved = true;
23935             }
23936             if((y + h) > vh+s.top){
23937                 y = vh - h - this.shadowOffset;
23938                 moved = true;
23939             }
23940             // then make sure top/left isn't negative
23941             if(x < s.left){
23942                 x = s.left;
23943                 moved = true;
23944             }
23945             if(y < s.top){
23946                 y = s.top;
23947                 moved = true;
23948             }
23949             if(moved){
23950                 if(this.avoidY){
23951                     var ay = this.avoidY;
23952                     if(y <= ay && (y+h) >= ay){
23953                         y = ay-h-5;   
23954                     }
23955                 }
23956                 xy = [x, y];
23957                 this.storeXY(xy);
23958                 supr.setXY.call(this, xy);
23959                 this.sync();
23960             }
23961         }
23962     },
23963
23964     isVisible : function(){
23965         return this.visible;    
23966     },
23967
23968     // private
23969     showAction : function(){
23970         this.visible = true; // track visibility to prevent getStyle calls
23971         if(this.useDisplay === true){
23972             this.setDisplayed("");
23973         }else if(this.lastXY){
23974             supr.setXY.call(this, this.lastXY);
23975         }else if(this.lastLT){
23976             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23977         }
23978     },
23979
23980     // private
23981     hideAction : function(){
23982         this.visible = false;
23983         if(this.useDisplay === true){
23984             this.setDisplayed(false);
23985         }else{
23986             this.setLeftTop(-10000,-10000);
23987         }
23988     },
23989
23990     // overridden Element method
23991     setVisible : function(v, a, d, c, e){
23992         if(v){
23993             this.showAction();
23994         }
23995         if(a && v){
23996             var cb = function(){
23997                 this.sync(true);
23998                 if(c){
23999                     c();
24000                 }
24001             }.createDelegate(this);
24002             supr.setVisible.call(this, true, true, d, cb, e);
24003         }else{
24004             if(!v){
24005                 this.hideUnders(true);
24006             }
24007             var cb = c;
24008             if(a){
24009                 cb = function(){
24010                     this.hideAction();
24011                     if(c){
24012                         c();
24013                     }
24014                 }.createDelegate(this);
24015             }
24016             supr.setVisible.call(this, v, a, d, cb, e);
24017             if(v){
24018                 this.sync(true);
24019             }else if(!a){
24020                 this.hideAction();
24021             }
24022         }
24023     },
24024
24025     storeXY : function(xy){
24026         delete this.lastLT;
24027         this.lastXY = xy;
24028     },
24029
24030     storeLeftTop : function(left, top){
24031         delete this.lastXY;
24032         this.lastLT = [left, top];
24033     },
24034
24035     // private
24036     beforeFx : function(){
24037         this.beforeAction();
24038         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24039     },
24040
24041     // private
24042     afterFx : function(){
24043         Roo.Layer.superclass.afterFx.apply(this, arguments);
24044         this.sync(this.isVisible());
24045     },
24046
24047     // private
24048     beforeAction : function(){
24049         if(!this.updating && this.shadow){
24050             this.shadow.hide();
24051         }
24052     },
24053
24054     // overridden Element method
24055     setLeft : function(left){
24056         this.storeLeftTop(left, this.getTop(true));
24057         supr.setLeft.apply(this, arguments);
24058         this.sync();
24059     },
24060
24061     setTop : function(top){
24062         this.storeLeftTop(this.getLeft(true), top);
24063         supr.setTop.apply(this, arguments);
24064         this.sync();
24065     },
24066
24067     setLeftTop : function(left, top){
24068         this.storeLeftTop(left, top);
24069         supr.setLeftTop.apply(this, arguments);
24070         this.sync();
24071     },
24072
24073     setXY : function(xy, a, d, c, e){
24074         this.fixDisplay();
24075         this.beforeAction();
24076         this.storeXY(xy);
24077         var cb = this.createCB(c);
24078         supr.setXY.call(this, xy, a, d, cb, e);
24079         if(!a){
24080             cb();
24081         }
24082     },
24083
24084     // private
24085     createCB : function(c){
24086         var el = this;
24087         return function(){
24088             el.constrainXY();
24089             el.sync(true);
24090             if(c){
24091                 c();
24092             }
24093         };
24094     },
24095
24096     // overridden Element method
24097     setX : function(x, a, d, c, e){
24098         this.setXY([x, this.getY()], a, d, c, e);
24099     },
24100
24101     // overridden Element method
24102     setY : function(y, a, d, c, e){
24103         this.setXY([this.getX(), y], a, d, c, e);
24104     },
24105
24106     // overridden Element method
24107     setSize : function(w, h, a, d, c, e){
24108         this.beforeAction();
24109         var cb = this.createCB(c);
24110         supr.setSize.call(this, w, h, a, d, cb, e);
24111         if(!a){
24112             cb();
24113         }
24114     },
24115
24116     // overridden Element method
24117     setWidth : function(w, a, d, c, e){
24118         this.beforeAction();
24119         var cb = this.createCB(c);
24120         supr.setWidth.call(this, w, a, d, cb, e);
24121         if(!a){
24122             cb();
24123         }
24124     },
24125
24126     // overridden Element method
24127     setHeight : function(h, a, d, c, e){
24128         this.beforeAction();
24129         var cb = this.createCB(c);
24130         supr.setHeight.call(this, h, a, d, cb, e);
24131         if(!a){
24132             cb();
24133         }
24134     },
24135
24136     // overridden Element method
24137     setBounds : function(x, y, w, h, a, d, c, e){
24138         this.beforeAction();
24139         var cb = this.createCB(c);
24140         if(!a){
24141             this.storeXY([x, y]);
24142             supr.setXY.call(this, [x, y]);
24143             supr.setSize.call(this, w, h, a, d, cb, e);
24144             cb();
24145         }else{
24146             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24147         }
24148         return this;
24149     },
24150     
24151     /**
24152      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24153      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24154      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24155      * @param {Number} zindex The new z-index to set
24156      * @return {this} The Layer
24157      */
24158     setZIndex : function(zindex){
24159         this.zindex = zindex;
24160         this.setStyle("z-index", zindex + 2);
24161         if(this.shadow){
24162             this.shadow.setZIndex(zindex + 1);
24163         }
24164         if(this.shim){
24165             this.shim.setStyle("z-index", zindex);
24166         }
24167     }
24168 });
24169 })();/*
24170  * Based on:
24171  * Ext JS Library 1.1.1
24172  * Copyright(c) 2006-2007, Ext JS, LLC.
24173  *
24174  * Originally Released Under LGPL - original licence link has changed is not relivant.
24175  *
24176  * Fork - LGPL
24177  * <script type="text/javascript">
24178  */
24179
24180
24181 /**
24182  * @class Roo.Shadow
24183  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24184  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24185  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24186  * @constructor
24187  * Create a new Shadow
24188  * @param {Object} config The config object
24189  */
24190 Roo.Shadow = function(config){
24191     Roo.apply(this, config);
24192     if(typeof this.mode != "string"){
24193         this.mode = this.defaultMode;
24194     }
24195     var o = this.offset, a = {h: 0};
24196     var rad = Math.floor(this.offset/2);
24197     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24198         case "drop":
24199             a.w = 0;
24200             a.l = a.t = o;
24201             a.t -= 1;
24202             if(Roo.isIE){
24203                 a.l -= this.offset + rad;
24204                 a.t -= this.offset + rad;
24205                 a.w -= rad;
24206                 a.h -= rad;
24207                 a.t += 1;
24208             }
24209         break;
24210         case "sides":
24211             a.w = (o*2);
24212             a.l = -o;
24213             a.t = o-1;
24214             if(Roo.isIE){
24215                 a.l -= (this.offset - rad);
24216                 a.t -= this.offset + rad;
24217                 a.l += 1;
24218                 a.w -= (this.offset - rad)*2;
24219                 a.w -= rad + 1;
24220                 a.h -= 1;
24221             }
24222         break;
24223         case "frame":
24224             a.w = a.h = (o*2);
24225             a.l = a.t = -o;
24226             a.t += 1;
24227             a.h -= 2;
24228             if(Roo.isIE){
24229                 a.l -= (this.offset - rad);
24230                 a.t -= (this.offset - rad);
24231                 a.l += 1;
24232                 a.w -= (this.offset + rad + 1);
24233                 a.h -= (this.offset + rad);
24234                 a.h += 1;
24235             }
24236         break;
24237     };
24238
24239     this.adjusts = a;
24240 };
24241
24242 Roo.Shadow.prototype = {
24243     /**
24244      * @cfg {String} mode
24245      * The shadow display mode.  Supports the following options:<br />
24246      * sides: Shadow displays on both sides and bottom only<br />
24247      * frame: Shadow displays equally on all four sides<br />
24248      * drop: Traditional bottom-right drop shadow (default)
24249      */
24250     /**
24251      * @cfg {String} offset
24252      * The number of pixels to offset the shadow from the element (defaults to 4)
24253      */
24254     offset: 4,
24255
24256     // private
24257     defaultMode: "drop",
24258
24259     /**
24260      * Displays the shadow under the target element
24261      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24262      */
24263     show : function(target){
24264         target = Roo.get(target);
24265         if(!this.el){
24266             this.el = Roo.Shadow.Pool.pull();
24267             if(this.el.dom.nextSibling != target.dom){
24268                 this.el.insertBefore(target);
24269             }
24270         }
24271         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24272         if(Roo.isIE){
24273             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24274         }
24275         this.realign(
24276             target.getLeft(true),
24277             target.getTop(true),
24278             target.getWidth(),
24279             target.getHeight()
24280         );
24281         this.el.dom.style.display = "block";
24282     },
24283
24284     /**
24285      * Returns true if the shadow is visible, else false
24286      */
24287     isVisible : function(){
24288         return this.el ? true : false;  
24289     },
24290
24291     /**
24292      * Direct alignment when values are already available. Show must be called at least once before
24293      * calling this method to ensure it is initialized.
24294      * @param {Number} left The target element left position
24295      * @param {Number} top The target element top position
24296      * @param {Number} width The target element width
24297      * @param {Number} height The target element height
24298      */
24299     realign : function(l, t, w, h){
24300         if(!this.el){
24301             return;
24302         }
24303         var a = this.adjusts, d = this.el.dom, s = d.style;
24304         var iea = 0;
24305         s.left = (l+a.l)+"px";
24306         s.top = (t+a.t)+"px";
24307         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24308  
24309         if(s.width != sws || s.height != shs){
24310             s.width = sws;
24311             s.height = shs;
24312             if(!Roo.isIE){
24313                 var cn = d.childNodes;
24314                 var sww = Math.max(0, (sw-12))+"px";
24315                 cn[0].childNodes[1].style.width = sww;
24316                 cn[1].childNodes[1].style.width = sww;
24317                 cn[2].childNodes[1].style.width = sww;
24318                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24319             }
24320         }
24321     },
24322
24323     /**
24324      * Hides this shadow
24325      */
24326     hide : function(){
24327         if(this.el){
24328             this.el.dom.style.display = "none";
24329             Roo.Shadow.Pool.push(this.el);
24330             delete this.el;
24331         }
24332     },
24333
24334     /**
24335      * Adjust the z-index of this shadow
24336      * @param {Number} zindex The new z-index
24337      */
24338     setZIndex : function(z){
24339         this.zIndex = z;
24340         if(this.el){
24341             this.el.setStyle("z-index", z);
24342         }
24343     }
24344 };
24345
24346 // Private utility class that manages the internal Shadow cache
24347 Roo.Shadow.Pool = function(){
24348     var p = [];
24349     var markup = Roo.isIE ?
24350                  '<div class="x-ie-shadow"></div>' :
24351                  '<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>';
24352     return {
24353         pull : function(){
24354             var sh = p.shift();
24355             if(!sh){
24356                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24357                 sh.autoBoxAdjust = false;
24358             }
24359             return sh;
24360         },
24361
24362         push : function(sh){
24363             p.push(sh);
24364         }
24365     };
24366 }();/*
24367  * Based on:
24368  * Ext JS Library 1.1.1
24369  * Copyright(c) 2006-2007, Ext JS, LLC.
24370  *
24371  * Originally Released Under LGPL - original licence link has changed is not relivant.
24372  *
24373  * Fork - LGPL
24374  * <script type="text/javascript">
24375  */
24376
24377
24378 /**
24379  * @class Roo.SplitBar
24380  * @extends Roo.util.Observable
24381  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24382  * <br><br>
24383  * Usage:
24384  * <pre><code>
24385 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24386                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24387 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24388 split.minSize = 100;
24389 split.maxSize = 600;
24390 split.animate = true;
24391 split.on('moved', splitterMoved);
24392 </code></pre>
24393  * @constructor
24394  * Create a new SplitBar
24395  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24396  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24397  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24398  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24399                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24400                         position of the SplitBar).
24401  */
24402 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24403     
24404     /** @private */
24405     this.el = Roo.get(dragElement, true);
24406     this.el.dom.unselectable = "on";
24407     /** @private */
24408     this.resizingEl = Roo.get(resizingElement, true);
24409
24410     /**
24411      * @private
24412      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24413      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24414      * @type Number
24415      */
24416     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24417     
24418     /**
24419      * The minimum size of the resizing element. (Defaults to 0)
24420      * @type Number
24421      */
24422     this.minSize = 0;
24423     
24424     /**
24425      * The maximum size of the resizing element. (Defaults to 2000)
24426      * @type Number
24427      */
24428     this.maxSize = 2000;
24429     
24430     /**
24431      * Whether to animate the transition to the new size
24432      * @type Boolean
24433      */
24434     this.animate = false;
24435     
24436     /**
24437      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24438      * @type Boolean
24439      */
24440     this.useShim = false;
24441     
24442     /** @private */
24443     this.shim = null;
24444     
24445     if(!existingProxy){
24446         /** @private */
24447         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24448     }else{
24449         this.proxy = Roo.get(existingProxy).dom;
24450     }
24451     /** @private */
24452     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24453     
24454     /** @private */
24455     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24456     
24457     /** @private */
24458     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24459     
24460     /** @private */
24461     this.dragSpecs = {};
24462     
24463     /**
24464      * @private The adapter to use to positon and resize elements
24465      */
24466     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24467     this.adapter.init(this);
24468     
24469     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24470         /** @private */
24471         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24472         this.el.addClass("x-splitbar-h");
24473     }else{
24474         /** @private */
24475         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24476         this.el.addClass("x-splitbar-v");
24477     }
24478     
24479     this.addEvents({
24480         /**
24481          * @event resize
24482          * Fires when the splitter is moved (alias for {@link #event-moved})
24483          * @param {Roo.SplitBar} this
24484          * @param {Number} newSize the new width or height
24485          */
24486         "resize" : true,
24487         /**
24488          * @event moved
24489          * Fires when the splitter is moved
24490          * @param {Roo.SplitBar} this
24491          * @param {Number} newSize the new width or height
24492          */
24493         "moved" : true,
24494         /**
24495          * @event beforeresize
24496          * Fires before the splitter is dragged
24497          * @param {Roo.SplitBar} this
24498          */
24499         "beforeresize" : true,
24500
24501         "beforeapply" : true
24502     });
24503
24504     Roo.util.Observable.call(this);
24505 };
24506
24507 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24508     onStartProxyDrag : function(x, y){
24509         this.fireEvent("beforeresize", this);
24510         if(!this.overlay){
24511             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24512             o.unselectable();
24513             o.enableDisplayMode("block");
24514             // all splitbars share the same overlay
24515             Roo.SplitBar.prototype.overlay = o;
24516         }
24517         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24518         this.overlay.show();
24519         Roo.get(this.proxy).setDisplayed("block");
24520         var size = this.adapter.getElementSize(this);
24521         this.activeMinSize = this.getMinimumSize();;
24522         this.activeMaxSize = this.getMaximumSize();;
24523         var c1 = size - this.activeMinSize;
24524         var c2 = Math.max(this.activeMaxSize - size, 0);
24525         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24526             this.dd.resetConstraints();
24527             this.dd.setXConstraint(
24528                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24529                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24530             );
24531             this.dd.setYConstraint(0, 0);
24532         }else{
24533             this.dd.resetConstraints();
24534             this.dd.setXConstraint(0, 0);
24535             this.dd.setYConstraint(
24536                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24537                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24538             );
24539          }
24540         this.dragSpecs.startSize = size;
24541         this.dragSpecs.startPoint = [x, y];
24542         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24543     },
24544     
24545     /** 
24546      * @private Called after the drag operation by the DDProxy
24547      */
24548     onEndProxyDrag : function(e){
24549         Roo.get(this.proxy).setDisplayed(false);
24550         var endPoint = Roo.lib.Event.getXY(e);
24551         if(this.overlay){
24552             this.overlay.hide();
24553         }
24554         var newSize;
24555         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24556             newSize = this.dragSpecs.startSize + 
24557                 (this.placement == Roo.SplitBar.LEFT ?
24558                     endPoint[0] - this.dragSpecs.startPoint[0] :
24559                     this.dragSpecs.startPoint[0] - endPoint[0]
24560                 );
24561         }else{
24562             newSize = this.dragSpecs.startSize + 
24563                 (this.placement == Roo.SplitBar.TOP ?
24564                     endPoint[1] - this.dragSpecs.startPoint[1] :
24565                     this.dragSpecs.startPoint[1] - endPoint[1]
24566                 );
24567         }
24568         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24569         if(newSize != this.dragSpecs.startSize){
24570             if(this.fireEvent('beforeapply', this, newSize) !== false){
24571                 this.adapter.setElementSize(this, newSize);
24572                 this.fireEvent("moved", this, newSize);
24573                 this.fireEvent("resize", this, newSize);
24574             }
24575         }
24576     },
24577     
24578     /**
24579      * Get the adapter this SplitBar uses
24580      * @return The adapter object
24581      */
24582     getAdapter : function(){
24583         return this.adapter;
24584     },
24585     
24586     /**
24587      * Set the adapter this SplitBar uses
24588      * @param {Object} adapter A SplitBar adapter object
24589      */
24590     setAdapter : function(adapter){
24591         this.adapter = adapter;
24592         this.adapter.init(this);
24593     },
24594     
24595     /**
24596      * Gets the minimum size for the resizing element
24597      * @return {Number} The minimum size
24598      */
24599     getMinimumSize : function(){
24600         return this.minSize;
24601     },
24602     
24603     /**
24604      * Sets the minimum size for the resizing element
24605      * @param {Number} minSize The minimum size
24606      */
24607     setMinimumSize : function(minSize){
24608         this.minSize = minSize;
24609     },
24610     
24611     /**
24612      * Gets the maximum size for the resizing element
24613      * @return {Number} The maximum size
24614      */
24615     getMaximumSize : function(){
24616         return this.maxSize;
24617     },
24618     
24619     /**
24620      * Sets the maximum size for the resizing element
24621      * @param {Number} maxSize The maximum size
24622      */
24623     setMaximumSize : function(maxSize){
24624         this.maxSize = maxSize;
24625     },
24626     
24627     /**
24628      * Sets the initialize size for the resizing element
24629      * @param {Number} size The initial size
24630      */
24631     setCurrentSize : function(size){
24632         var oldAnimate = this.animate;
24633         this.animate = false;
24634         this.adapter.setElementSize(this, size);
24635         this.animate = oldAnimate;
24636     },
24637     
24638     /**
24639      * Destroy this splitbar. 
24640      * @param {Boolean} removeEl True to remove the element
24641      */
24642     destroy : function(removeEl){
24643         if(this.shim){
24644             this.shim.remove();
24645         }
24646         this.dd.unreg();
24647         this.proxy.parentNode.removeChild(this.proxy);
24648         if(removeEl){
24649             this.el.remove();
24650         }
24651     }
24652 });
24653
24654 /**
24655  * @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.
24656  */
24657 Roo.SplitBar.createProxy = function(dir){
24658     var proxy = new Roo.Element(document.createElement("div"));
24659     proxy.unselectable();
24660     var cls = 'x-splitbar-proxy';
24661     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24662     document.body.appendChild(proxy.dom);
24663     return proxy.dom;
24664 };
24665
24666 /** 
24667  * @class Roo.SplitBar.BasicLayoutAdapter
24668  * Default Adapter. It assumes the splitter and resizing element are not positioned
24669  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24670  */
24671 Roo.SplitBar.BasicLayoutAdapter = function(){
24672 };
24673
24674 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24675     // do nothing for now
24676     init : function(s){
24677     
24678     },
24679     /**
24680      * Called before drag operations to get the current size of the resizing element. 
24681      * @param {Roo.SplitBar} s The SplitBar using this adapter
24682      */
24683      getElementSize : function(s){
24684         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24685             return s.resizingEl.getWidth();
24686         }else{
24687             return s.resizingEl.getHeight();
24688         }
24689     },
24690     
24691     /**
24692      * Called after drag operations to set the size of the resizing element.
24693      * @param {Roo.SplitBar} s The SplitBar using this adapter
24694      * @param {Number} newSize The new size to set
24695      * @param {Function} onComplete A function to be invoked when resizing is complete
24696      */
24697     setElementSize : function(s, newSize, onComplete){
24698         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24699             if(!s.animate){
24700                 s.resizingEl.setWidth(newSize);
24701                 if(onComplete){
24702                     onComplete(s, newSize);
24703                 }
24704             }else{
24705                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24706             }
24707         }else{
24708             
24709             if(!s.animate){
24710                 s.resizingEl.setHeight(newSize);
24711                 if(onComplete){
24712                     onComplete(s, newSize);
24713                 }
24714             }else{
24715                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24716             }
24717         }
24718     }
24719 };
24720
24721 /** 
24722  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24723  * @extends Roo.SplitBar.BasicLayoutAdapter
24724  * Adapter that  moves the splitter element to align with the resized sizing element. 
24725  * Used with an absolute positioned SplitBar.
24726  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24727  * document.body, make sure you assign an id to the body element.
24728  */
24729 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24730     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24731     this.container = Roo.get(container);
24732 };
24733
24734 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24735     init : function(s){
24736         this.basic.init(s);
24737     },
24738     
24739     getElementSize : function(s){
24740         return this.basic.getElementSize(s);
24741     },
24742     
24743     setElementSize : function(s, newSize, onComplete){
24744         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24745     },
24746     
24747     moveSplitter : function(s){
24748         var yes = Roo.SplitBar;
24749         switch(s.placement){
24750             case yes.LEFT:
24751                 s.el.setX(s.resizingEl.getRight());
24752                 break;
24753             case yes.RIGHT:
24754                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24755                 break;
24756             case yes.TOP:
24757                 s.el.setY(s.resizingEl.getBottom());
24758                 break;
24759             case yes.BOTTOM:
24760                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24761                 break;
24762         }
24763     }
24764 };
24765
24766 /**
24767  * Orientation constant - Create a vertical SplitBar
24768  * @static
24769  * @type Number
24770  */
24771 Roo.SplitBar.VERTICAL = 1;
24772
24773 /**
24774  * Orientation constant - Create a horizontal SplitBar
24775  * @static
24776  * @type Number
24777  */
24778 Roo.SplitBar.HORIZONTAL = 2;
24779
24780 /**
24781  * Placement constant - The resizing element is to the left of the splitter element
24782  * @static
24783  * @type Number
24784  */
24785 Roo.SplitBar.LEFT = 1;
24786
24787 /**
24788  * Placement constant - The resizing element is to the right of the splitter element
24789  * @static
24790  * @type Number
24791  */
24792 Roo.SplitBar.RIGHT = 2;
24793
24794 /**
24795  * Placement constant - The resizing element is positioned above the splitter element
24796  * @static
24797  * @type Number
24798  */
24799 Roo.SplitBar.TOP = 3;
24800
24801 /**
24802  * Placement constant - The resizing element is positioned under splitter element
24803  * @static
24804  * @type Number
24805  */
24806 Roo.SplitBar.BOTTOM = 4;
24807 /*
24808  * Based on:
24809  * Ext JS Library 1.1.1
24810  * Copyright(c) 2006-2007, Ext JS, LLC.
24811  *
24812  * Originally Released Under LGPL - original licence link has changed is not relivant.
24813  *
24814  * Fork - LGPL
24815  * <script type="text/javascript">
24816  */
24817
24818 /**
24819  * @class Roo.View
24820  * @extends Roo.util.Observable
24821  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24822  * This class also supports single and multi selection modes. <br>
24823  * Create a data model bound view:
24824  <pre><code>
24825  var store = new Roo.data.Store(...);
24826
24827  var view = new Roo.View({
24828     el : "my-element",
24829     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24830  
24831     singleSelect: true,
24832     selectedClass: "ydataview-selected",
24833     store: store
24834  });
24835
24836  // listen for node click?
24837  view.on("click", function(vw, index, node, e){
24838  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24839  });
24840
24841  // load XML data
24842  dataModel.load("foobar.xml");
24843  </code></pre>
24844  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24845  * <br><br>
24846  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24847  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24848  * 
24849  * Note: old style constructor is still suported (container, template, config)
24850  * 
24851  * @constructor
24852  * Create a new View
24853  * @param {Object} config The config object
24854  * 
24855  */
24856 Roo.View = function(config, depreciated_tpl, depreciated_config){
24857     
24858     this.parent = false;
24859     
24860     if (typeof(depreciated_tpl) == 'undefined') {
24861         // new way.. - universal constructor.
24862         Roo.apply(this, config);
24863         this.el  = Roo.get(this.el);
24864     } else {
24865         // old format..
24866         this.el  = Roo.get(config);
24867         this.tpl = depreciated_tpl;
24868         Roo.apply(this, depreciated_config);
24869     }
24870     this.wrapEl  = this.el.wrap().wrap();
24871     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24872     
24873     
24874     if(typeof(this.tpl) == "string"){
24875         this.tpl = new Roo.Template(this.tpl);
24876     } else {
24877         // support xtype ctors..
24878         this.tpl = new Roo.factory(this.tpl, Roo);
24879     }
24880     
24881     
24882     this.tpl.compile();
24883     
24884     /** @private */
24885     this.addEvents({
24886         /**
24887          * @event beforeclick
24888          * Fires before a click is processed. Returns false to cancel the default action.
24889          * @param {Roo.View} this
24890          * @param {Number} index The index of the target node
24891          * @param {HTMLElement} node The target node
24892          * @param {Roo.EventObject} e The raw event object
24893          */
24894             "beforeclick" : true,
24895         /**
24896          * @event click
24897          * Fires when a template node is clicked.
24898          * @param {Roo.View} this
24899          * @param {Number} index The index of the target node
24900          * @param {HTMLElement} node The target node
24901          * @param {Roo.EventObject} e The raw event object
24902          */
24903             "click" : true,
24904         /**
24905          * @event dblclick
24906          * Fires when a template node is double clicked.
24907          * @param {Roo.View} this
24908          * @param {Number} index The index of the target node
24909          * @param {HTMLElement} node The target node
24910          * @param {Roo.EventObject} e The raw event object
24911          */
24912             "dblclick" : true,
24913         /**
24914          * @event contextmenu
24915          * Fires when a template node is right clicked.
24916          * @param {Roo.View} this
24917          * @param {Number} index The index of the target node
24918          * @param {HTMLElement} node The target node
24919          * @param {Roo.EventObject} e The raw event object
24920          */
24921             "contextmenu" : true,
24922         /**
24923          * @event selectionchange
24924          * Fires when the selected nodes change.
24925          * @param {Roo.View} this
24926          * @param {Array} selections Array of the selected nodes
24927          */
24928             "selectionchange" : true,
24929     
24930         /**
24931          * @event beforeselect
24932          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24933          * @param {Roo.View} this
24934          * @param {HTMLElement} node The node to be selected
24935          * @param {Array} selections Array of currently selected nodes
24936          */
24937             "beforeselect" : true,
24938         /**
24939          * @event preparedata
24940          * Fires on every row to render, to allow you to change the data.
24941          * @param {Roo.View} this
24942          * @param {Object} data to be rendered (change this)
24943          */
24944           "preparedata" : true
24945           
24946           
24947         });
24948
24949
24950
24951     this.el.on({
24952         "click": this.onClick,
24953         "dblclick": this.onDblClick,
24954         "contextmenu": this.onContextMenu,
24955         scope:this
24956     });
24957
24958     this.selections = [];
24959     this.nodes = [];
24960     this.cmp = new Roo.CompositeElementLite([]);
24961     if(this.store){
24962         this.store = Roo.factory(this.store, Roo.data);
24963         this.setStore(this.store, true);
24964     }
24965     
24966     if ( this.footer && this.footer.xtype) {
24967            
24968          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24969         
24970         this.footer.dataSource = this.store
24971         this.footer.container = fctr;
24972         this.footer = Roo.factory(this.footer, Roo);
24973         fctr.insertFirst(this.el);
24974         
24975         // this is a bit insane - as the paging toolbar seems to detach the el..
24976 //        dom.parentNode.parentNode.parentNode
24977          // they get detached?
24978     }
24979     
24980     
24981     Roo.View.superclass.constructor.call(this);
24982     
24983     
24984 };
24985
24986 Roo.extend(Roo.View, Roo.util.Observable, {
24987     
24988      /**
24989      * @cfg {Roo.data.Store} store Data store to load data from.
24990      */
24991     store : false,
24992     
24993     /**
24994      * @cfg {String|Roo.Element} el The container element.
24995      */
24996     el : '',
24997     
24998     /**
24999      * @cfg {String|Roo.Template} tpl The template used by this View 
25000      */
25001     tpl : false,
25002     /**
25003      * @cfg {String} dataName the named area of the template to use as the data area
25004      *                          Works with domtemplates roo-name="name"
25005      */
25006     dataName: false,
25007     /**
25008      * @cfg {String} selectedClass The css class to add to selected nodes
25009      */
25010     selectedClass : "x-view-selected",
25011      /**
25012      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25013      */
25014     emptyText : "",
25015     
25016     /**
25017      * @cfg {String} text to display on mask (default Loading)
25018      */
25019     mask : false,
25020     /**
25021      * @cfg {Boolean} multiSelect Allow multiple selection
25022      */
25023     multiSelect : false,
25024     /**
25025      * @cfg {Boolean} singleSelect Allow single selection
25026      */
25027     singleSelect:  false,
25028     
25029     /**
25030      * @cfg {Boolean} toggleSelect - selecting 
25031      */
25032     toggleSelect : false,
25033     
25034     /**
25035      * @cfg {Boolean} tickable - selecting 
25036      */
25037     tickable : false,
25038     
25039     /**
25040      * Returns the element this view is bound to.
25041      * @return {Roo.Element}
25042      */
25043     getEl : function(){
25044         return this.wrapEl;
25045     },
25046     
25047     
25048
25049     /**
25050      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25051      */
25052     refresh : function(){
25053         Roo.log('refresh');
25054         var t = this.tpl;
25055         
25056         // if we are using something like 'domtemplate', then
25057         // the what gets used is:
25058         // t.applySubtemplate(NAME, data, wrapping data..)
25059         // the outer template then get' applied with
25060         //     the store 'extra data'
25061         // and the body get's added to the
25062         //      roo-name="data" node?
25063         //      <span class='roo-tpl-{name}'></span> ?????
25064         
25065         
25066         
25067         this.clearSelections();
25068         this.el.update("");
25069         var html = [];
25070         var records = this.store.getRange();
25071         if(records.length < 1) {
25072             
25073             // is this valid??  = should it render a template??
25074             
25075             this.el.update(this.emptyText);
25076             return;
25077         }
25078         var el = this.el;
25079         if (this.dataName) {
25080             this.el.update(t.apply(this.store.meta)); //????
25081             el = this.el.child('.roo-tpl-' + this.dataName);
25082         }
25083         
25084         for(var i = 0, len = records.length; i < len; i++){
25085             var data = this.prepareData(records[i].data, i, records[i]);
25086             this.fireEvent("preparedata", this, data, i, records[i]);
25087             
25088             var d = Roo.apply({}, data);
25089             
25090             if(this.tickable){
25091                 Roo.apply(d, {'roo-id' : Roo.id()});
25092                 
25093                 var _this = this;
25094             
25095                 Roo.each(this.parent.item, function(item){
25096                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25097                         return;
25098                     }
25099                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25100                 });
25101             }
25102             
25103             html[html.length] = Roo.util.Format.trim(
25104                 this.dataName ?
25105                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25106                     t.apply(d)
25107             );
25108         }
25109         
25110         
25111         
25112         el.update(html.join(""));
25113         this.nodes = el.dom.childNodes;
25114         this.updateIndexes(0);
25115     },
25116     
25117
25118     /**
25119      * Function to override to reformat the data that is sent to
25120      * the template for each node.
25121      * DEPRICATED - use the preparedata event handler.
25122      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25123      * a JSON object for an UpdateManager bound view).
25124      */
25125     prepareData : function(data, index, record)
25126     {
25127         this.fireEvent("preparedata", this, data, index, record);
25128         return data;
25129     },
25130
25131     onUpdate : function(ds, record){
25132          Roo.log('on update');   
25133         this.clearSelections();
25134         var index = this.store.indexOf(record);
25135         var n = this.nodes[index];
25136         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25137         n.parentNode.removeChild(n);
25138         this.updateIndexes(index, index);
25139     },
25140
25141     
25142     
25143 // --------- FIXME     
25144     onAdd : function(ds, records, index)
25145     {
25146         Roo.log(['on Add', ds, records, index] );        
25147         this.clearSelections();
25148         if(this.nodes.length == 0){
25149             this.refresh();
25150             return;
25151         }
25152         var n = this.nodes[index];
25153         for(var i = 0, len = records.length; i < len; i++){
25154             var d = this.prepareData(records[i].data, i, records[i]);
25155             if(n){
25156                 this.tpl.insertBefore(n, d);
25157             }else{
25158                 
25159                 this.tpl.append(this.el, d);
25160             }
25161         }
25162         this.updateIndexes(index);
25163     },
25164
25165     onRemove : function(ds, record, index){
25166         Roo.log('onRemove');
25167         this.clearSelections();
25168         var el = this.dataName  ?
25169             this.el.child('.roo-tpl-' + this.dataName) :
25170             this.el; 
25171         
25172         el.dom.removeChild(this.nodes[index]);
25173         this.updateIndexes(index);
25174     },
25175
25176     /**
25177      * Refresh an individual node.
25178      * @param {Number} index
25179      */
25180     refreshNode : function(index){
25181         this.onUpdate(this.store, this.store.getAt(index));
25182     },
25183
25184     updateIndexes : function(startIndex, endIndex){
25185         var ns = this.nodes;
25186         startIndex = startIndex || 0;
25187         endIndex = endIndex || ns.length - 1;
25188         for(var i = startIndex; i <= endIndex; i++){
25189             ns[i].nodeIndex = i;
25190         }
25191     },
25192
25193     /**
25194      * Changes the data store this view uses and refresh the view.
25195      * @param {Store} store
25196      */
25197     setStore : function(store, initial){
25198         if(!initial && this.store){
25199             this.store.un("datachanged", this.refresh);
25200             this.store.un("add", this.onAdd);
25201             this.store.un("remove", this.onRemove);
25202             this.store.un("update", this.onUpdate);
25203             this.store.un("clear", this.refresh);
25204             this.store.un("beforeload", this.onBeforeLoad);
25205             this.store.un("load", this.onLoad);
25206             this.store.un("loadexception", this.onLoad);
25207         }
25208         if(store){
25209           
25210             store.on("datachanged", this.refresh, this);
25211             store.on("add", this.onAdd, this);
25212             store.on("remove", this.onRemove, this);
25213             store.on("update", this.onUpdate, this);
25214             store.on("clear", this.refresh, this);
25215             store.on("beforeload", this.onBeforeLoad, this);
25216             store.on("load", this.onLoad, this);
25217             store.on("loadexception", this.onLoad, this);
25218         }
25219         
25220         if(store){
25221             this.refresh();
25222         }
25223     },
25224     /**
25225      * onbeforeLoad - masks the loading area.
25226      *
25227      */
25228     onBeforeLoad : function(store,opts)
25229     {
25230          Roo.log('onBeforeLoad');   
25231         if (!opts.add) {
25232             this.el.update("");
25233         }
25234         this.el.mask(this.mask ? this.mask : "Loading" ); 
25235     },
25236     onLoad : function ()
25237     {
25238         this.el.unmask();
25239     },
25240     
25241
25242     /**
25243      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25244      * @param {HTMLElement} node
25245      * @return {HTMLElement} The template node
25246      */
25247     findItemFromChild : function(node){
25248         var el = this.dataName  ?
25249             this.el.child('.roo-tpl-' + this.dataName,true) :
25250             this.el.dom; 
25251         
25252         if(!node || node.parentNode == el){
25253                     return node;
25254             }
25255             var p = node.parentNode;
25256             while(p && p != el){
25257             if(p.parentNode == el){
25258                 return p;
25259             }
25260             p = p.parentNode;
25261         }
25262             return null;
25263     },
25264
25265     /** @ignore */
25266     onClick : function(e){
25267         var item = this.findItemFromChild(e.getTarget());
25268         if(item){
25269             var index = this.indexOf(item);
25270             if(this.onItemClick(item, index, e) !== false){
25271                 this.fireEvent("click", this, index, item, e);
25272             }
25273         }else{
25274             this.clearSelections();
25275         }
25276     },
25277
25278     /** @ignore */
25279     onContextMenu : function(e){
25280         var item = this.findItemFromChild(e.getTarget());
25281         if(item){
25282             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25283         }
25284     },
25285
25286     /** @ignore */
25287     onDblClick : function(e){
25288         var item = this.findItemFromChild(e.getTarget());
25289         if(item){
25290             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25291         }
25292     },
25293
25294     onItemClick : function(item, index, e)
25295     {
25296         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25297             return false;
25298         }
25299         if (this.toggleSelect) {
25300             var m = this.isSelected(item) ? 'unselect' : 'select';
25301             Roo.log(m);
25302             var _t = this;
25303             _t[m](item, true, false);
25304             return true;
25305         }
25306         if(this.multiSelect || this.singleSelect){
25307             if(this.multiSelect && e.shiftKey && this.lastSelection){
25308                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25309             }else{
25310                 this.select(item, this.multiSelect && e.ctrlKey);
25311                 this.lastSelection = item;
25312             }
25313             
25314             if(!this.tickable){
25315                 e.preventDefault();
25316             }
25317             
25318         }
25319         return true;
25320     },
25321
25322     /**
25323      * Get the number of selected nodes.
25324      * @return {Number}
25325      */
25326     getSelectionCount : function(){
25327         return this.selections.length;
25328     },
25329
25330     /**
25331      * Get the currently selected nodes.
25332      * @return {Array} An array of HTMLElements
25333      */
25334     getSelectedNodes : function(){
25335         return this.selections;
25336     },
25337
25338     /**
25339      * Get the indexes of the selected nodes.
25340      * @return {Array}
25341      */
25342     getSelectedIndexes : function(){
25343         var indexes = [], s = this.selections;
25344         for(var i = 0, len = s.length; i < len; i++){
25345             indexes.push(s[i].nodeIndex);
25346         }
25347         return indexes;
25348     },
25349
25350     /**
25351      * Clear all selections
25352      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25353      */
25354     clearSelections : function(suppressEvent){
25355         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25356             this.cmp.elements = this.selections;
25357             this.cmp.removeClass(this.selectedClass);
25358             this.selections = [];
25359             if(!suppressEvent){
25360                 this.fireEvent("selectionchange", this, this.selections);
25361             }
25362         }
25363     },
25364
25365     /**
25366      * Returns true if the passed node is selected
25367      * @param {HTMLElement/Number} node The node or node index
25368      * @return {Boolean}
25369      */
25370     isSelected : function(node){
25371         var s = this.selections;
25372         if(s.length < 1){
25373             return false;
25374         }
25375         node = this.getNode(node);
25376         return s.indexOf(node) !== -1;
25377     },
25378
25379     /**
25380      * Selects nodes.
25381      * @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
25382      * @param {Boolean} keepExisting (optional) true to keep existing selections
25383      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25384      */
25385     select : function(nodeInfo, keepExisting, suppressEvent){
25386         if(nodeInfo instanceof Array){
25387             if(!keepExisting){
25388                 this.clearSelections(true);
25389             }
25390             for(var i = 0, len = nodeInfo.length; i < len; i++){
25391                 this.select(nodeInfo[i], true, true);
25392             }
25393             return;
25394         } 
25395         var node = this.getNode(nodeInfo);
25396         if(!node || this.isSelected(node)){
25397             return; // already selected.
25398         }
25399         if(!keepExisting){
25400             this.clearSelections(true);
25401         }
25402         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25403             Roo.fly(node).addClass(this.selectedClass);
25404             this.selections.push(node);
25405             if(!suppressEvent){
25406                 this.fireEvent("selectionchange", this, this.selections);
25407             }
25408         }
25409         
25410         
25411     },
25412       /**
25413      * Unselects nodes.
25414      * @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
25415      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25416      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25417      */
25418     unselect : function(nodeInfo, keepExisting, suppressEvent)
25419     {
25420         if(nodeInfo instanceof Array){
25421             Roo.each(this.selections, function(s) {
25422                 this.unselect(s, nodeInfo);
25423             }, this);
25424             return;
25425         }
25426         var node = this.getNode(nodeInfo);
25427         if(!node || !this.isSelected(node)){
25428             Roo.log("not selected");
25429             return; // not selected.
25430         }
25431         // fireevent???
25432         var ns = [];
25433         Roo.each(this.selections, function(s) {
25434             if (s == node ) {
25435                 Roo.fly(node).removeClass(this.selectedClass);
25436
25437                 return;
25438             }
25439             ns.push(s);
25440         },this);
25441         
25442         this.selections= ns;
25443         this.fireEvent("selectionchange", this, this.selections);
25444     },
25445
25446     /**
25447      * Gets a template node.
25448      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25449      * @return {HTMLElement} The node or null if it wasn't found
25450      */
25451     getNode : function(nodeInfo){
25452         if(typeof nodeInfo == "string"){
25453             return document.getElementById(nodeInfo);
25454         }else if(typeof nodeInfo == "number"){
25455             return this.nodes[nodeInfo];
25456         }
25457         return nodeInfo;
25458     },
25459
25460     /**
25461      * Gets a range template nodes.
25462      * @param {Number} startIndex
25463      * @param {Number} endIndex
25464      * @return {Array} An array of nodes
25465      */
25466     getNodes : function(start, end){
25467         var ns = this.nodes;
25468         start = start || 0;
25469         end = typeof end == "undefined" ? ns.length - 1 : end;
25470         var nodes = [];
25471         if(start <= end){
25472             for(var i = start; i <= end; i++){
25473                 nodes.push(ns[i]);
25474             }
25475         } else{
25476             for(var i = start; i >= end; i--){
25477                 nodes.push(ns[i]);
25478             }
25479         }
25480         return nodes;
25481     },
25482
25483     /**
25484      * Finds the index of the passed node
25485      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25486      * @return {Number} The index of the node or -1
25487      */
25488     indexOf : function(node){
25489         node = this.getNode(node);
25490         if(typeof node.nodeIndex == "number"){
25491             return node.nodeIndex;
25492         }
25493         var ns = this.nodes;
25494         for(var i = 0, len = ns.length; i < len; i++){
25495             if(ns[i] == node){
25496                 return i;
25497             }
25498         }
25499         return -1;
25500     }
25501 });
25502 /*
25503  * Based on:
25504  * Ext JS Library 1.1.1
25505  * Copyright(c) 2006-2007, Ext JS, LLC.
25506  *
25507  * Originally Released Under LGPL - original licence link has changed is not relivant.
25508  *
25509  * Fork - LGPL
25510  * <script type="text/javascript">
25511  */
25512
25513 /**
25514  * @class Roo.JsonView
25515  * @extends Roo.View
25516  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25517 <pre><code>
25518 var view = new Roo.JsonView({
25519     container: "my-element",
25520     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25521     multiSelect: true, 
25522     jsonRoot: "data" 
25523 });
25524
25525 // listen for node click?
25526 view.on("click", function(vw, index, node, e){
25527     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25528 });
25529
25530 // direct load of JSON data
25531 view.load("foobar.php");
25532
25533 // Example from my blog list
25534 var tpl = new Roo.Template(
25535     '&lt;div class="entry"&gt;' +
25536     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25537     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25538     "&lt;/div&gt;&lt;hr /&gt;"
25539 );
25540
25541 var moreView = new Roo.JsonView({
25542     container :  "entry-list", 
25543     template : tpl,
25544     jsonRoot: "posts"
25545 });
25546 moreView.on("beforerender", this.sortEntries, this);
25547 moreView.load({
25548     url: "/blog/get-posts.php",
25549     params: "allposts=true",
25550     text: "Loading Blog Entries..."
25551 });
25552 </code></pre>
25553
25554 * Note: old code is supported with arguments : (container, template, config)
25555
25556
25557  * @constructor
25558  * Create a new JsonView
25559  * 
25560  * @param {Object} config The config object
25561  * 
25562  */
25563 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25564     
25565     
25566     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25567
25568     var um = this.el.getUpdateManager();
25569     um.setRenderer(this);
25570     um.on("update", this.onLoad, this);
25571     um.on("failure", this.onLoadException, this);
25572
25573     /**
25574      * @event beforerender
25575      * Fires before rendering of the downloaded JSON data.
25576      * @param {Roo.JsonView} this
25577      * @param {Object} data The JSON data loaded
25578      */
25579     /**
25580      * @event load
25581      * Fires when data is loaded.
25582      * @param {Roo.JsonView} this
25583      * @param {Object} data The JSON data loaded
25584      * @param {Object} response The raw Connect response object
25585      */
25586     /**
25587      * @event loadexception
25588      * Fires when loading fails.
25589      * @param {Roo.JsonView} this
25590      * @param {Object} response The raw Connect response object
25591      */
25592     this.addEvents({
25593         'beforerender' : true,
25594         'load' : true,
25595         'loadexception' : true
25596     });
25597 };
25598 Roo.extend(Roo.JsonView, Roo.View, {
25599     /**
25600      * @type {String} The root property in the loaded JSON object that contains the data
25601      */
25602     jsonRoot : "",
25603
25604     /**
25605      * Refreshes the view.
25606      */
25607     refresh : function(){
25608         this.clearSelections();
25609         this.el.update("");
25610         var html = [];
25611         var o = this.jsonData;
25612         if(o && o.length > 0){
25613             for(var i = 0, len = o.length; i < len; i++){
25614                 var data = this.prepareData(o[i], i, o);
25615                 html[html.length] = this.tpl.apply(data);
25616             }
25617         }else{
25618             html.push(this.emptyText);
25619         }
25620         this.el.update(html.join(""));
25621         this.nodes = this.el.dom.childNodes;
25622         this.updateIndexes(0);
25623     },
25624
25625     /**
25626      * 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.
25627      * @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:
25628      <pre><code>
25629      view.load({
25630          url: "your-url.php",
25631          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25632          callback: yourFunction,
25633          scope: yourObject, //(optional scope)
25634          discardUrl: false,
25635          nocache: false,
25636          text: "Loading...",
25637          timeout: 30,
25638          scripts: false
25639      });
25640      </code></pre>
25641      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25642      * 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.
25643      * @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}
25644      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25645      * @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.
25646      */
25647     load : function(){
25648         var um = this.el.getUpdateManager();
25649         um.update.apply(um, arguments);
25650     },
25651
25652     render : function(el, response){
25653         this.clearSelections();
25654         this.el.update("");
25655         var o;
25656         try{
25657             o = Roo.util.JSON.decode(response.responseText);
25658             if(this.jsonRoot){
25659                 
25660                 o = o[this.jsonRoot];
25661             }
25662         } catch(e){
25663         }
25664         /**
25665          * The current JSON data or null
25666          */
25667         this.jsonData = o;
25668         this.beforeRender();
25669         this.refresh();
25670     },
25671
25672 /**
25673  * Get the number of records in the current JSON dataset
25674  * @return {Number}
25675  */
25676     getCount : function(){
25677         return this.jsonData ? this.jsonData.length : 0;
25678     },
25679
25680 /**
25681  * Returns the JSON object for the specified node(s)
25682  * @param {HTMLElement/Array} node The node or an array of nodes
25683  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25684  * you get the JSON object for the node
25685  */
25686     getNodeData : function(node){
25687         if(node instanceof Array){
25688             var data = [];
25689             for(var i = 0, len = node.length; i < len; i++){
25690                 data.push(this.getNodeData(node[i]));
25691             }
25692             return data;
25693         }
25694         return this.jsonData[this.indexOf(node)] || null;
25695     },
25696
25697     beforeRender : function(){
25698         this.snapshot = this.jsonData;
25699         if(this.sortInfo){
25700             this.sort.apply(this, this.sortInfo);
25701         }
25702         this.fireEvent("beforerender", this, this.jsonData);
25703     },
25704
25705     onLoad : function(el, o){
25706         this.fireEvent("load", this, this.jsonData, o);
25707     },
25708
25709     onLoadException : function(el, o){
25710         this.fireEvent("loadexception", this, o);
25711     },
25712
25713 /**
25714  * Filter the data by a specific property.
25715  * @param {String} property A property on your JSON objects
25716  * @param {String/RegExp} value Either string that the property values
25717  * should start with, or a RegExp to test against the property
25718  */
25719     filter : function(property, value){
25720         if(this.jsonData){
25721             var data = [];
25722             var ss = this.snapshot;
25723             if(typeof value == "string"){
25724                 var vlen = value.length;
25725                 if(vlen == 0){
25726                     this.clearFilter();
25727                     return;
25728                 }
25729                 value = value.toLowerCase();
25730                 for(var i = 0, len = ss.length; i < len; i++){
25731                     var o = ss[i];
25732                     if(o[property].substr(0, vlen).toLowerCase() == value){
25733                         data.push(o);
25734                     }
25735                 }
25736             } else if(value.exec){ // regex?
25737                 for(var i = 0, len = ss.length; i < len; i++){
25738                     var o = ss[i];
25739                     if(value.test(o[property])){
25740                         data.push(o);
25741                     }
25742                 }
25743             } else{
25744                 return;
25745             }
25746             this.jsonData = data;
25747             this.refresh();
25748         }
25749     },
25750
25751 /**
25752  * Filter by a function. The passed function will be called with each
25753  * object in the current dataset. If the function returns true the value is kept,
25754  * otherwise it is filtered.
25755  * @param {Function} fn
25756  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25757  */
25758     filterBy : function(fn, scope){
25759         if(this.jsonData){
25760             var data = [];
25761             var ss = this.snapshot;
25762             for(var i = 0, len = ss.length; i < len; i++){
25763                 var o = ss[i];
25764                 if(fn.call(scope || this, o)){
25765                     data.push(o);
25766                 }
25767             }
25768             this.jsonData = data;
25769             this.refresh();
25770         }
25771     },
25772
25773 /**
25774  * Clears the current filter.
25775  */
25776     clearFilter : function(){
25777         if(this.snapshot && this.jsonData != this.snapshot){
25778             this.jsonData = this.snapshot;
25779             this.refresh();
25780         }
25781     },
25782
25783
25784 /**
25785  * Sorts the data for this view and refreshes it.
25786  * @param {String} property A property on your JSON objects to sort on
25787  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25788  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25789  */
25790     sort : function(property, dir, sortType){
25791         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25792         if(this.jsonData){
25793             var p = property;
25794             var dsc = dir && dir.toLowerCase() == "desc";
25795             var f = function(o1, o2){
25796                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25797                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25798                 ;
25799                 if(v1 < v2){
25800                     return dsc ? +1 : -1;
25801                 } else if(v1 > v2){
25802                     return dsc ? -1 : +1;
25803                 } else{
25804                     return 0;
25805                 }
25806             };
25807             this.jsonData.sort(f);
25808             this.refresh();
25809             if(this.jsonData != this.snapshot){
25810                 this.snapshot.sort(f);
25811             }
25812         }
25813     }
25814 });/*
25815  * Based on:
25816  * Ext JS Library 1.1.1
25817  * Copyright(c) 2006-2007, Ext JS, LLC.
25818  *
25819  * Originally Released Under LGPL - original licence link has changed is not relivant.
25820  *
25821  * Fork - LGPL
25822  * <script type="text/javascript">
25823  */
25824  
25825
25826 /**
25827  * @class Roo.ColorPalette
25828  * @extends Roo.Component
25829  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25830  * Here's an example of typical usage:
25831  * <pre><code>
25832 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25833 cp.render('my-div');
25834
25835 cp.on('select', function(palette, selColor){
25836     // do something with selColor
25837 });
25838 </code></pre>
25839  * @constructor
25840  * Create a new ColorPalette
25841  * @param {Object} config The config object
25842  */
25843 Roo.ColorPalette = function(config){
25844     Roo.ColorPalette.superclass.constructor.call(this, config);
25845     this.addEvents({
25846         /**
25847              * @event select
25848              * Fires when a color is selected
25849              * @param {ColorPalette} this
25850              * @param {String} color The 6-digit color hex code (without the # symbol)
25851              */
25852         select: true
25853     });
25854
25855     if(this.handler){
25856         this.on("select", this.handler, this.scope, true);
25857     }
25858 };
25859 Roo.extend(Roo.ColorPalette, Roo.Component, {
25860     /**
25861      * @cfg {String} itemCls
25862      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25863      */
25864     itemCls : "x-color-palette",
25865     /**
25866      * @cfg {String} value
25867      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25868      * the hex codes are case-sensitive.
25869      */
25870     value : null,
25871     clickEvent:'click',
25872     // private
25873     ctype: "Roo.ColorPalette",
25874
25875     /**
25876      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25877      */
25878     allowReselect : false,
25879
25880     /**
25881      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25882      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25883      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25884      * of colors with the width setting until the box is symmetrical.</p>
25885      * <p>You can override individual colors if needed:</p>
25886      * <pre><code>
25887 var cp = new Roo.ColorPalette();
25888 cp.colors[0] = "FF0000";  // change the first box to red
25889 </code></pre>
25890
25891 Or you can provide a custom array of your own for complete control:
25892 <pre><code>
25893 var cp = new Roo.ColorPalette();
25894 cp.colors = ["000000", "993300", "333300"];
25895 </code></pre>
25896      * @type Array
25897      */
25898     colors : [
25899         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25900         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25901         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25902         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25903         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25904     ],
25905
25906     // private
25907     onRender : function(container, position){
25908         var t = new Roo.MasterTemplate(
25909             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25910         );
25911         var c = this.colors;
25912         for(var i = 0, len = c.length; i < len; i++){
25913             t.add([c[i]]);
25914         }
25915         var el = document.createElement("div");
25916         el.className = this.itemCls;
25917         t.overwrite(el);
25918         container.dom.insertBefore(el, position);
25919         this.el = Roo.get(el);
25920         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25921         if(this.clickEvent != 'click'){
25922             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25923         }
25924     },
25925
25926     // private
25927     afterRender : function(){
25928         Roo.ColorPalette.superclass.afterRender.call(this);
25929         if(this.value){
25930             var s = this.value;
25931             this.value = null;
25932             this.select(s);
25933         }
25934     },
25935
25936     // private
25937     handleClick : function(e, t){
25938         e.preventDefault();
25939         if(!this.disabled){
25940             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25941             this.select(c.toUpperCase());
25942         }
25943     },
25944
25945     /**
25946      * Selects the specified color in the palette (fires the select event)
25947      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25948      */
25949     select : function(color){
25950         color = color.replace("#", "");
25951         if(color != this.value || this.allowReselect){
25952             var el = this.el;
25953             if(this.value){
25954                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25955             }
25956             el.child("a.color-"+color).addClass("x-color-palette-sel");
25957             this.value = color;
25958             this.fireEvent("select", this, color);
25959         }
25960     }
25961 });/*
25962  * Based on:
25963  * Ext JS Library 1.1.1
25964  * Copyright(c) 2006-2007, Ext JS, LLC.
25965  *
25966  * Originally Released Under LGPL - original licence link has changed is not relivant.
25967  *
25968  * Fork - LGPL
25969  * <script type="text/javascript">
25970  */
25971  
25972 /**
25973  * @class Roo.DatePicker
25974  * @extends Roo.Component
25975  * Simple date picker class.
25976  * @constructor
25977  * Create a new DatePicker
25978  * @param {Object} config The config object
25979  */
25980 Roo.DatePicker = function(config){
25981     Roo.DatePicker.superclass.constructor.call(this, config);
25982
25983     this.value = config && config.value ?
25984                  config.value.clearTime() : new Date().clearTime();
25985
25986     this.addEvents({
25987         /**
25988              * @event select
25989              * Fires when a date is selected
25990              * @param {DatePicker} this
25991              * @param {Date} date The selected date
25992              */
25993         'select': true,
25994         /**
25995              * @event monthchange
25996              * Fires when the displayed month changes 
25997              * @param {DatePicker} this
25998              * @param {Date} date The selected month
25999              */
26000         'monthchange': true
26001     });
26002
26003     if(this.handler){
26004         this.on("select", this.handler,  this.scope || this);
26005     }
26006     // build the disabledDatesRE
26007     if(!this.disabledDatesRE && this.disabledDates){
26008         var dd = this.disabledDates;
26009         var re = "(?:";
26010         for(var i = 0; i < dd.length; i++){
26011             re += dd[i];
26012             if(i != dd.length-1) re += "|";
26013         }
26014         this.disabledDatesRE = new RegExp(re + ")");
26015     }
26016 };
26017
26018 Roo.extend(Roo.DatePicker, Roo.Component, {
26019     /**
26020      * @cfg {String} todayText
26021      * The text to display on the button that selects the current date (defaults to "Today")
26022      */
26023     todayText : "Today",
26024     /**
26025      * @cfg {String} okText
26026      * The text to display on the ok button
26027      */
26028     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26029     /**
26030      * @cfg {String} cancelText
26031      * The text to display on the cancel button
26032      */
26033     cancelText : "Cancel",
26034     /**
26035      * @cfg {String} todayTip
26036      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26037      */
26038     todayTip : "{0} (Spacebar)",
26039     /**
26040      * @cfg {Date} minDate
26041      * Minimum allowable date (JavaScript date object, defaults to null)
26042      */
26043     minDate : null,
26044     /**
26045      * @cfg {Date} maxDate
26046      * Maximum allowable date (JavaScript date object, defaults to null)
26047      */
26048     maxDate : null,
26049     /**
26050      * @cfg {String} minText
26051      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26052      */
26053     minText : "This date is before the minimum date",
26054     /**
26055      * @cfg {String} maxText
26056      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26057      */
26058     maxText : "This date is after the maximum date",
26059     /**
26060      * @cfg {String} format
26061      * The default date format string which can be overriden for localization support.  The format must be
26062      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26063      */
26064     format : "m/d/y",
26065     /**
26066      * @cfg {Array} disabledDays
26067      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26068      */
26069     disabledDays : null,
26070     /**
26071      * @cfg {String} disabledDaysText
26072      * The tooltip to display when the date falls on a disabled day (defaults to "")
26073      */
26074     disabledDaysText : "",
26075     /**
26076      * @cfg {RegExp} disabledDatesRE
26077      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26078      */
26079     disabledDatesRE : null,
26080     /**
26081      * @cfg {String} disabledDatesText
26082      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26083      */
26084     disabledDatesText : "",
26085     /**
26086      * @cfg {Boolean} constrainToViewport
26087      * True to constrain the date picker to the viewport (defaults to true)
26088      */
26089     constrainToViewport : true,
26090     /**
26091      * @cfg {Array} monthNames
26092      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26093      */
26094     monthNames : Date.monthNames,
26095     /**
26096      * @cfg {Array} dayNames
26097      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26098      */
26099     dayNames : Date.dayNames,
26100     /**
26101      * @cfg {String} nextText
26102      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26103      */
26104     nextText: 'Next Month (Control+Right)',
26105     /**
26106      * @cfg {String} prevText
26107      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26108      */
26109     prevText: 'Previous Month (Control+Left)',
26110     /**
26111      * @cfg {String} monthYearText
26112      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26113      */
26114     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26115     /**
26116      * @cfg {Number} startDay
26117      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26118      */
26119     startDay : 0,
26120     /**
26121      * @cfg {Bool} showClear
26122      * Show a clear button (usefull for date form elements that can be blank.)
26123      */
26124     
26125     showClear: false,
26126     
26127     /**
26128      * Sets the value of the date field
26129      * @param {Date} value The date to set
26130      */
26131     setValue : function(value){
26132         var old = this.value;
26133         
26134         if (typeof(value) == 'string') {
26135          
26136             value = Date.parseDate(value, this.format);
26137         }
26138         if (!value) {
26139             value = new Date();
26140         }
26141         
26142         this.value = value.clearTime(true);
26143         if(this.el){
26144             this.update(this.value);
26145         }
26146     },
26147
26148     /**
26149      * Gets the current selected value of the date field
26150      * @return {Date} The selected date
26151      */
26152     getValue : function(){
26153         return this.value;
26154     },
26155
26156     // private
26157     focus : function(){
26158         if(this.el){
26159             this.update(this.activeDate);
26160         }
26161     },
26162
26163     // privateval
26164     onRender : function(container, position){
26165         
26166         var m = [
26167              '<table cellspacing="0">',
26168                 '<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>',
26169                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26170         var dn = this.dayNames;
26171         for(var i = 0; i < 7; i++){
26172             var d = this.startDay+i;
26173             if(d > 6){
26174                 d = d-7;
26175             }
26176             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26177         }
26178         m[m.length] = "</tr></thead><tbody><tr>";
26179         for(var i = 0; i < 42; i++) {
26180             if(i % 7 == 0 && i != 0){
26181                 m[m.length] = "</tr><tr>";
26182             }
26183             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26184         }
26185         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26186             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26187
26188         var el = document.createElement("div");
26189         el.className = "x-date-picker";
26190         el.innerHTML = m.join("");
26191
26192         container.dom.insertBefore(el, position);
26193
26194         this.el = Roo.get(el);
26195         this.eventEl = Roo.get(el.firstChild);
26196
26197         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26198             handler: this.showPrevMonth,
26199             scope: this,
26200             preventDefault:true,
26201             stopDefault:true
26202         });
26203
26204         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26205             handler: this.showNextMonth,
26206             scope: this,
26207             preventDefault:true,
26208             stopDefault:true
26209         });
26210
26211         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26212
26213         this.monthPicker = this.el.down('div.x-date-mp');
26214         this.monthPicker.enableDisplayMode('block');
26215         
26216         var kn = new Roo.KeyNav(this.eventEl, {
26217             "left" : function(e){
26218                 e.ctrlKey ?
26219                     this.showPrevMonth() :
26220                     this.update(this.activeDate.add("d", -1));
26221             },
26222
26223             "right" : function(e){
26224                 e.ctrlKey ?
26225                     this.showNextMonth() :
26226                     this.update(this.activeDate.add("d", 1));
26227             },
26228
26229             "up" : function(e){
26230                 e.ctrlKey ?
26231                     this.showNextYear() :
26232                     this.update(this.activeDate.add("d", -7));
26233             },
26234
26235             "down" : function(e){
26236                 e.ctrlKey ?
26237                     this.showPrevYear() :
26238                     this.update(this.activeDate.add("d", 7));
26239             },
26240
26241             "pageUp" : function(e){
26242                 this.showNextMonth();
26243             },
26244
26245             "pageDown" : function(e){
26246                 this.showPrevMonth();
26247             },
26248
26249             "enter" : function(e){
26250                 e.stopPropagation();
26251                 return true;
26252             },
26253
26254             scope : this
26255         });
26256
26257         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26258
26259         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26260
26261         this.el.unselectable();
26262         
26263         this.cells = this.el.select("table.x-date-inner tbody td");
26264         this.textNodes = this.el.query("table.x-date-inner tbody span");
26265
26266         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26267             text: "&#160;",
26268             tooltip: this.monthYearText
26269         });
26270
26271         this.mbtn.on('click', this.showMonthPicker, this);
26272         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26273
26274
26275         var today = (new Date()).dateFormat(this.format);
26276         
26277         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26278         if (this.showClear) {
26279             baseTb.add( new Roo.Toolbar.Fill());
26280         }
26281         baseTb.add({
26282             text: String.format(this.todayText, today),
26283             tooltip: String.format(this.todayTip, today),
26284             handler: this.selectToday,
26285             scope: this
26286         });
26287         
26288         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26289             
26290         //});
26291         if (this.showClear) {
26292             
26293             baseTb.add( new Roo.Toolbar.Fill());
26294             baseTb.add({
26295                 text: '&#160;',
26296                 cls: 'x-btn-icon x-btn-clear',
26297                 handler: function() {
26298                     //this.value = '';
26299                     this.fireEvent("select", this, '');
26300                 },
26301                 scope: this
26302             });
26303         }
26304         
26305         
26306         if(Roo.isIE){
26307             this.el.repaint();
26308         }
26309         this.update(this.value);
26310     },
26311
26312     createMonthPicker : function(){
26313         if(!this.monthPicker.dom.firstChild){
26314             var buf = ['<table border="0" cellspacing="0">'];
26315             for(var i = 0; i < 6; i++){
26316                 buf.push(
26317                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26318                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26319                     i == 0 ?
26320                     '<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>' :
26321                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26322                 );
26323             }
26324             buf.push(
26325                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26326                     this.okText,
26327                     '</button><button type="button" class="x-date-mp-cancel">',
26328                     this.cancelText,
26329                     '</button></td></tr>',
26330                 '</table>'
26331             );
26332             this.monthPicker.update(buf.join(''));
26333             this.monthPicker.on('click', this.onMonthClick, this);
26334             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26335
26336             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26337             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26338
26339             this.mpMonths.each(function(m, a, i){
26340                 i += 1;
26341                 if((i%2) == 0){
26342                     m.dom.xmonth = 5 + Math.round(i * .5);
26343                 }else{
26344                     m.dom.xmonth = Math.round((i-1) * .5);
26345                 }
26346             });
26347         }
26348     },
26349
26350     showMonthPicker : function(){
26351         this.createMonthPicker();
26352         var size = this.el.getSize();
26353         this.monthPicker.setSize(size);
26354         this.monthPicker.child('table').setSize(size);
26355
26356         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26357         this.updateMPMonth(this.mpSelMonth);
26358         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26359         this.updateMPYear(this.mpSelYear);
26360
26361         this.monthPicker.slideIn('t', {duration:.2});
26362     },
26363
26364     updateMPYear : function(y){
26365         this.mpyear = y;
26366         var ys = this.mpYears.elements;
26367         for(var i = 1; i <= 10; i++){
26368             var td = ys[i-1], y2;
26369             if((i%2) == 0){
26370                 y2 = y + Math.round(i * .5);
26371                 td.firstChild.innerHTML = y2;
26372                 td.xyear = y2;
26373             }else{
26374                 y2 = y - (5-Math.round(i * .5));
26375                 td.firstChild.innerHTML = y2;
26376                 td.xyear = y2;
26377             }
26378             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26379         }
26380     },
26381
26382     updateMPMonth : function(sm){
26383         this.mpMonths.each(function(m, a, i){
26384             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26385         });
26386     },
26387
26388     selectMPMonth: function(m){
26389         
26390     },
26391
26392     onMonthClick : function(e, t){
26393         e.stopEvent();
26394         var el = new Roo.Element(t), pn;
26395         if(el.is('button.x-date-mp-cancel')){
26396             this.hideMonthPicker();
26397         }
26398         else if(el.is('button.x-date-mp-ok')){
26399             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26400             this.hideMonthPicker();
26401         }
26402         else if(pn = el.up('td.x-date-mp-month', 2)){
26403             this.mpMonths.removeClass('x-date-mp-sel');
26404             pn.addClass('x-date-mp-sel');
26405             this.mpSelMonth = pn.dom.xmonth;
26406         }
26407         else if(pn = el.up('td.x-date-mp-year', 2)){
26408             this.mpYears.removeClass('x-date-mp-sel');
26409             pn.addClass('x-date-mp-sel');
26410             this.mpSelYear = pn.dom.xyear;
26411         }
26412         else if(el.is('a.x-date-mp-prev')){
26413             this.updateMPYear(this.mpyear-10);
26414         }
26415         else if(el.is('a.x-date-mp-next')){
26416             this.updateMPYear(this.mpyear+10);
26417         }
26418     },
26419
26420     onMonthDblClick : function(e, t){
26421         e.stopEvent();
26422         var el = new Roo.Element(t), pn;
26423         if(pn = el.up('td.x-date-mp-month', 2)){
26424             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26425             this.hideMonthPicker();
26426         }
26427         else if(pn = el.up('td.x-date-mp-year', 2)){
26428             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26429             this.hideMonthPicker();
26430         }
26431     },
26432
26433     hideMonthPicker : function(disableAnim){
26434         if(this.monthPicker){
26435             if(disableAnim === true){
26436                 this.monthPicker.hide();
26437             }else{
26438                 this.monthPicker.slideOut('t', {duration:.2});
26439             }
26440         }
26441     },
26442
26443     // private
26444     showPrevMonth : function(e){
26445         this.update(this.activeDate.add("mo", -1));
26446     },
26447
26448     // private
26449     showNextMonth : function(e){
26450         this.update(this.activeDate.add("mo", 1));
26451     },
26452
26453     // private
26454     showPrevYear : function(){
26455         this.update(this.activeDate.add("y", -1));
26456     },
26457
26458     // private
26459     showNextYear : function(){
26460         this.update(this.activeDate.add("y", 1));
26461     },
26462
26463     // private
26464     handleMouseWheel : function(e){
26465         var delta = e.getWheelDelta();
26466         if(delta > 0){
26467             this.showPrevMonth();
26468             e.stopEvent();
26469         } else if(delta < 0){
26470             this.showNextMonth();
26471             e.stopEvent();
26472         }
26473     },
26474
26475     // private
26476     handleDateClick : function(e, t){
26477         e.stopEvent();
26478         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26479             this.setValue(new Date(t.dateValue));
26480             this.fireEvent("select", this, this.value);
26481         }
26482     },
26483
26484     // private
26485     selectToday : function(){
26486         this.setValue(new Date().clearTime());
26487         this.fireEvent("select", this, this.value);
26488     },
26489
26490     // private
26491     update : function(date)
26492     {
26493         var vd = this.activeDate;
26494         this.activeDate = date;
26495         if(vd && this.el){
26496             var t = date.getTime();
26497             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26498                 this.cells.removeClass("x-date-selected");
26499                 this.cells.each(function(c){
26500                    if(c.dom.firstChild.dateValue == t){
26501                        c.addClass("x-date-selected");
26502                        setTimeout(function(){
26503                             try{c.dom.firstChild.focus();}catch(e){}
26504                        }, 50);
26505                        return false;
26506                    }
26507                 });
26508                 return;
26509             }
26510         }
26511         
26512         var days = date.getDaysInMonth();
26513         var firstOfMonth = date.getFirstDateOfMonth();
26514         var startingPos = firstOfMonth.getDay()-this.startDay;
26515
26516         if(startingPos <= this.startDay){
26517             startingPos += 7;
26518         }
26519
26520         var pm = date.add("mo", -1);
26521         var prevStart = pm.getDaysInMonth()-startingPos;
26522
26523         var cells = this.cells.elements;
26524         var textEls = this.textNodes;
26525         days += startingPos;
26526
26527         // convert everything to numbers so it's fast
26528         var day = 86400000;
26529         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26530         var today = new Date().clearTime().getTime();
26531         var sel = date.clearTime().getTime();
26532         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26533         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26534         var ddMatch = this.disabledDatesRE;
26535         var ddText = this.disabledDatesText;
26536         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26537         var ddaysText = this.disabledDaysText;
26538         var format = this.format;
26539
26540         var setCellClass = function(cal, cell){
26541             cell.title = "";
26542             var t = d.getTime();
26543             cell.firstChild.dateValue = t;
26544             if(t == today){
26545                 cell.className += " x-date-today";
26546                 cell.title = cal.todayText;
26547             }
26548             if(t == sel){
26549                 cell.className += " x-date-selected";
26550                 setTimeout(function(){
26551                     try{cell.firstChild.focus();}catch(e){}
26552                 }, 50);
26553             }
26554             // disabling
26555             if(t < min) {
26556                 cell.className = " x-date-disabled";
26557                 cell.title = cal.minText;
26558                 return;
26559             }
26560             if(t > max) {
26561                 cell.className = " x-date-disabled";
26562                 cell.title = cal.maxText;
26563                 return;
26564             }
26565             if(ddays){
26566                 if(ddays.indexOf(d.getDay()) != -1){
26567                     cell.title = ddaysText;
26568                     cell.className = " x-date-disabled";
26569                 }
26570             }
26571             if(ddMatch && format){
26572                 var fvalue = d.dateFormat(format);
26573                 if(ddMatch.test(fvalue)){
26574                     cell.title = ddText.replace("%0", fvalue);
26575                     cell.className = " x-date-disabled";
26576                 }
26577             }
26578         };
26579
26580         var i = 0;
26581         for(; i < startingPos; i++) {
26582             textEls[i].innerHTML = (++prevStart);
26583             d.setDate(d.getDate()+1);
26584             cells[i].className = "x-date-prevday";
26585             setCellClass(this, cells[i]);
26586         }
26587         for(; i < days; i++){
26588             intDay = i - startingPos + 1;
26589             textEls[i].innerHTML = (intDay);
26590             d.setDate(d.getDate()+1);
26591             cells[i].className = "x-date-active";
26592             setCellClass(this, cells[i]);
26593         }
26594         var extraDays = 0;
26595         for(; i < 42; i++) {
26596              textEls[i].innerHTML = (++extraDays);
26597              d.setDate(d.getDate()+1);
26598              cells[i].className = "x-date-nextday";
26599              setCellClass(this, cells[i]);
26600         }
26601
26602         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26603         this.fireEvent('monthchange', this, date);
26604         
26605         if(!this.internalRender){
26606             var main = this.el.dom.firstChild;
26607             var w = main.offsetWidth;
26608             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26609             Roo.fly(main).setWidth(w);
26610             this.internalRender = true;
26611             // opera does not respect the auto grow header center column
26612             // then, after it gets a width opera refuses to recalculate
26613             // without a second pass
26614             if(Roo.isOpera && !this.secondPass){
26615                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26616                 this.secondPass = true;
26617                 this.update.defer(10, this, [date]);
26618             }
26619         }
26620         
26621         
26622     }
26623 });        /*
26624  * Based on:
26625  * Ext JS Library 1.1.1
26626  * Copyright(c) 2006-2007, Ext JS, LLC.
26627  *
26628  * Originally Released Under LGPL - original licence link has changed is not relivant.
26629  *
26630  * Fork - LGPL
26631  * <script type="text/javascript">
26632  */
26633 /**
26634  * @class Roo.TabPanel
26635  * @extends Roo.util.Observable
26636  * A lightweight tab container.
26637  * <br><br>
26638  * Usage:
26639  * <pre><code>
26640 // basic tabs 1, built from existing content
26641 var tabs = new Roo.TabPanel("tabs1");
26642 tabs.addTab("script", "View Script");
26643 tabs.addTab("markup", "View Markup");
26644 tabs.activate("script");
26645
26646 // more advanced tabs, built from javascript
26647 var jtabs = new Roo.TabPanel("jtabs");
26648 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26649
26650 // set up the UpdateManager
26651 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26652 var updater = tab2.getUpdateManager();
26653 updater.setDefaultUrl("ajax1.htm");
26654 tab2.on('activate', updater.refresh, updater, true);
26655
26656 // Use setUrl for Ajax loading
26657 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26658 tab3.setUrl("ajax2.htm", null, true);
26659
26660 // Disabled tab
26661 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26662 tab4.disable();
26663
26664 jtabs.activate("jtabs-1");
26665  * </code></pre>
26666  * @constructor
26667  * Create a new TabPanel.
26668  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26669  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26670  */
26671 Roo.TabPanel = function(container, config){
26672     /**
26673     * The container element for this TabPanel.
26674     * @type Roo.Element
26675     */
26676     this.el = Roo.get(container, true);
26677     if(config){
26678         if(typeof config == "boolean"){
26679             this.tabPosition = config ? "bottom" : "top";
26680         }else{
26681             Roo.apply(this, config);
26682         }
26683     }
26684     if(this.tabPosition == "bottom"){
26685         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26686         this.el.addClass("x-tabs-bottom");
26687     }
26688     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26689     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26690     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26691     if(Roo.isIE){
26692         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26693     }
26694     if(this.tabPosition != "bottom"){
26695         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26696          * @type Roo.Element
26697          */
26698         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26699         this.el.addClass("x-tabs-top");
26700     }
26701     this.items = [];
26702
26703     this.bodyEl.setStyle("position", "relative");
26704
26705     this.active = null;
26706     this.activateDelegate = this.activate.createDelegate(this);
26707
26708     this.addEvents({
26709         /**
26710          * @event tabchange
26711          * Fires when the active tab changes
26712          * @param {Roo.TabPanel} this
26713          * @param {Roo.TabPanelItem} activePanel The new active tab
26714          */
26715         "tabchange": true,
26716         /**
26717          * @event beforetabchange
26718          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26719          * @param {Roo.TabPanel} this
26720          * @param {Object} e Set cancel to true on this object to cancel the tab change
26721          * @param {Roo.TabPanelItem} tab The tab being changed to
26722          */
26723         "beforetabchange" : true
26724     });
26725
26726     Roo.EventManager.onWindowResize(this.onResize, this);
26727     this.cpad = this.el.getPadding("lr");
26728     this.hiddenCount = 0;
26729
26730
26731     // toolbar on the tabbar support...
26732     if (this.toolbar) {
26733         var tcfg = this.toolbar;
26734         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26735         this.toolbar = new Roo.Toolbar(tcfg);
26736         if (Roo.isSafari) {
26737             var tbl = tcfg.container.child('table', true);
26738             tbl.setAttribute('width', '100%');
26739         }
26740         
26741     }
26742    
26743
26744
26745     Roo.TabPanel.superclass.constructor.call(this);
26746 };
26747
26748 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26749     /*
26750      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26751      */
26752     tabPosition : "top",
26753     /*
26754      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26755      */
26756     currentTabWidth : 0,
26757     /*
26758      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26759      */
26760     minTabWidth : 40,
26761     /*
26762      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26763      */
26764     maxTabWidth : 250,
26765     /*
26766      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26767      */
26768     preferredTabWidth : 175,
26769     /*
26770      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26771      */
26772     resizeTabs : false,
26773     /*
26774      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26775      */
26776     monitorResize : true,
26777     /*
26778      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26779      */
26780     toolbar : false,
26781
26782     /**
26783      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26784      * @param {String} id The id of the div to use <b>or create</b>
26785      * @param {String} text The text for the tab
26786      * @param {String} content (optional) Content to put in the TabPanelItem body
26787      * @param {Boolean} closable (optional) True to create a close icon on the tab
26788      * @return {Roo.TabPanelItem} The created TabPanelItem
26789      */
26790     addTab : function(id, text, content, closable){
26791         var item = new Roo.TabPanelItem(this, id, text, closable);
26792         this.addTabItem(item);
26793         if(content){
26794             item.setContent(content);
26795         }
26796         return item;
26797     },
26798
26799     /**
26800      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26801      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26802      * @return {Roo.TabPanelItem}
26803      */
26804     getTab : function(id){
26805         return this.items[id];
26806     },
26807
26808     /**
26809      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26810      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26811      */
26812     hideTab : function(id){
26813         var t = this.items[id];
26814         if(!t.isHidden()){
26815            t.setHidden(true);
26816            this.hiddenCount++;
26817            this.autoSizeTabs();
26818         }
26819     },
26820
26821     /**
26822      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26823      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26824      */
26825     unhideTab : function(id){
26826         var t = this.items[id];
26827         if(t.isHidden()){
26828            t.setHidden(false);
26829            this.hiddenCount--;
26830            this.autoSizeTabs();
26831         }
26832     },
26833
26834     /**
26835      * Adds an existing {@link Roo.TabPanelItem}.
26836      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26837      */
26838     addTabItem : function(item){
26839         this.items[item.id] = item;
26840         this.items.push(item);
26841         if(this.resizeTabs){
26842            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26843            this.autoSizeTabs();
26844         }else{
26845             item.autoSize();
26846         }
26847     },
26848
26849     /**
26850      * Removes a {@link Roo.TabPanelItem}.
26851      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26852      */
26853     removeTab : function(id){
26854         var items = this.items;
26855         var tab = items[id];
26856         if(!tab) { return; }
26857         var index = items.indexOf(tab);
26858         if(this.active == tab && items.length > 1){
26859             var newTab = this.getNextAvailable(index);
26860             if(newTab) {
26861                 newTab.activate();
26862             }
26863         }
26864         this.stripEl.dom.removeChild(tab.pnode.dom);
26865         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26866             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26867         }
26868         items.splice(index, 1);
26869         delete this.items[tab.id];
26870         tab.fireEvent("close", tab);
26871         tab.purgeListeners();
26872         this.autoSizeTabs();
26873     },
26874
26875     getNextAvailable : function(start){
26876         var items = this.items;
26877         var index = start;
26878         // look for a next tab that will slide over to
26879         // replace the one being removed
26880         while(index < items.length){
26881             var item = items[++index];
26882             if(item && !item.isHidden()){
26883                 return item;
26884             }
26885         }
26886         // if one isn't found select the previous tab (on the left)
26887         index = start;
26888         while(index >= 0){
26889             var item = items[--index];
26890             if(item && !item.isHidden()){
26891                 return item;
26892             }
26893         }
26894         return null;
26895     },
26896
26897     /**
26898      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26899      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26900      */
26901     disableTab : function(id){
26902         var tab = this.items[id];
26903         if(tab && this.active != tab){
26904             tab.disable();
26905         }
26906     },
26907
26908     /**
26909      * Enables a {@link Roo.TabPanelItem} that is disabled.
26910      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26911      */
26912     enableTab : function(id){
26913         var tab = this.items[id];
26914         tab.enable();
26915     },
26916
26917     /**
26918      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26919      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26920      * @return {Roo.TabPanelItem} The TabPanelItem.
26921      */
26922     activate : function(id){
26923         var tab = this.items[id];
26924         if(!tab){
26925             return null;
26926         }
26927         if(tab == this.active || tab.disabled){
26928             return tab;
26929         }
26930         var e = {};
26931         this.fireEvent("beforetabchange", this, e, tab);
26932         if(e.cancel !== true && !tab.disabled){
26933             if(this.active){
26934                 this.active.hide();
26935             }
26936             this.active = this.items[id];
26937             this.active.show();
26938             this.fireEvent("tabchange", this, this.active);
26939         }
26940         return tab;
26941     },
26942
26943     /**
26944      * Gets the active {@link Roo.TabPanelItem}.
26945      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26946      */
26947     getActiveTab : function(){
26948         return this.active;
26949     },
26950
26951     /**
26952      * Updates the tab body element to fit the height of the container element
26953      * for overflow scrolling
26954      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26955      */
26956     syncHeight : function(targetHeight){
26957         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26958         var bm = this.bodyEl.getMargins();
26959         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26960         this.bodyEl.setHeight(newHeight);
26961         return newHeight;
26962     },
26963
26964     onResize : function(){
26965         if(this.monitorResize){
26966             this.autoSizeTabs();
26967         }
26968     },
26969
26970     /**
26971      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26972      */
26973     beginUpdate : function(){
26974         this.updating = true;
26975     },
26976
26977     /**
26978      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26979      */
26980     endUpdate : function(){
26981         this.updating = false;
26982         this.autoSizeTabs();
26983     },
26984
26985     /**
26986      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26987      */
26988     autoSizeTabs : function(){
26989         var count = this.items.length;
26990         var vcount = count - this.hiddenCount;
26991         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26992         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26993         var availWidth = Math.floor(w / vcount);
26994         var b = this.stripBody;
26995         if(b.getWidth() > w){
26996             var tabs = this.items;
26997             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26998             if(availWidth < this.minTabWidth){
26999                 /*if(!this.sleft){    // incomplete scrolling code
27000                     this.createScrollButtons();
27001                 }
27002                 this.showScroll();
27003                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27004             }
27005         }else{
27006             if(this.currentTabWidth < this.preferredTabWidth){
27007                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27008             }
27009         }
27010     },
27011
27012     /**
27013      * Returns the number of tabs in this TabPanel.
27014      * @return {Number}
27015      */
27016      getCount : function(){
27017          return this.items.length;
27018      },
27019
27020     /**
27021      * Resizes all the tabs to the passed width
27022      * @param {Number} The new width
27023      */
27024     setTabWidth : function(width){
27025         this.currentTabWidth = width;
27026         for(var i = 0, len = this.items.length; i < len; i++) {
27027                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27028         }
27029     },
27030
27031     /**
27032      * Destroys this TabPanel
27033      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27034      */
27035     destroy : function(removeEl){
27036         Roo.EventManager.removeResizeListener(this.onResize, this);
27037         for(var i = 0, len = this.items.length; i < len; i++){
27038             this.items[i].purgeListeners();
27039         }
27040         if(removeEl === true){
27041             this.el.update("");
27042             this.el.remove();
27043         }
27044     }
27045 });
27046
27047 /**
27048  * @class Roo.TabPanelItem
27049  * @extends Roo.util.Observable
27050  * Represents an individual item (tab plus body) in a TabPanel.
27051  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27052  * @param {String} id The id of this TabPanelItem
27053  * @param {String} text The text for the tab of this TabPanelItem
27054  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27055  */
27056 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27057     /**
27058      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27059      * @type Roo.TabPanel
27060      */
27061     this.tabPanel = tabPanel;
27062     /**
27063      * The id for this TabPanelItem
27064      * @type String
27065      */
27066     this.id = id;
27067     /** @private */
27068     this.disabled = false;
27069     /** @private */
27070     this.text = text;
27071     /** @private */
27072     this.loaded = false;
27073     this.closable = closable;
27074
27075     /**
27076      * The body element for this TabPanelItem.
27077      * @type Roo.Element
27078      */
27079     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27080     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27081     this.bodyEl.setStyle("display", "block");
27082     this.bodyEl.setStyle("zoom", "1");
27083     this.hideAction();
27084
27085     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27086     /** @private */
27087     this.el = Roo.get(els.el, true);
27088     this.inner = Roo.get(els.inner, true);
27089     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27090     this.pnode = Roo.get(els.el.parentNode, true);
27091     this.el.on("mousedown", this.onTabMouseDown, this);
27092     this.el.on("click", this.onTabClick, this);
27093     /** @private */
27094     if(closable){
27095         var c = Roo.get(els.close, true);
27096         c.dom.title = this.closeText;
27097         c.addClassOnOver("close-over");
27098         c.on("click", this.closeClick, this);
27099      }
27100
27101     this.addEvents({
27102          /**
27103          * @event activate
27104          * Fires when this tab becomes the active tab.
27105          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27106          * @param {Roo.TabPanelItem} this
27107          */
27108         "activate": true,
27109         /**
27110          * @event beforeclose
27111          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27112          * @param {Roo.TabPanelItem} this
27113          * @param {Object} e Set cancel to true on this object to cancel the close.
27114          */
27115         "beforeclose": true,
27116         /**
27117          * @event close
27118          * Fires when this tab is closed.
27119          * @param {Roo.TabPanelItem} this
27120          */
27121          "close": true,
27122         /**
27123          * @event deactivate
27124          * Fires when this tab is no longer the active tab.
27125          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27126          * @param {Roo.TabPanelItem} this
27127          */
27128          "deactivate" : true
27129     });
27130     this.hidden = false;
27131
27132     Roo.TabPanelItem.superclass.constructor.call(this);
27133 };
27134
27135 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27136     purgeListeners : function(){
27137        Roo.util.Observable.prototype.purgeListeners.call(this);
27138        this.el.removeAllListeners();
27139     },
27140     /**
27141      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27142      */
27143     show : function(){
27144         this.pnode.addClass("on");
27145         this.showAction();
27146         if(Roo.isOpera){
27147             this.tabPanel.stripWrap.repaint();
27148         }
27149         this.fireEvent("activate", this.tabPanel, this);
27150     },
27151
27152     /**
27153      * Returns true if this tab is the active tab.
27154      * @return {Boolean}
27155      */
27156     isActive : function(){
27157         return this.tabPanel.getActiveTab() == this;
27158     },
27159
27160     /**
27161      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27162      */
27163     hide : function(){
27164         this.pnode.removeClass("on");
27165         this.hideAction();
27166         this.fireEvent("deactivate", this.tabPanel, this);
27167     },
27168
27169     hideAction : function(){
27170         this.bodyEl.hide();
27171         this.bodyEl.setStyle("position", "absolute");
27172         this.bodyEl.setLeft("-20000px");
27173         this.bodyEl.setTop("-20000px");
27174     },
27175
27176     showAction : function(){
27177         this.bodyEl.setStyle("position", "relative");
27178         this.bodyEl.setTop("");
27179         this.bodyEl.setLeft("");
27180         this.bodyEl.show();
27181     },
27182
27183     /**
27184      * Set the tooltip for the tab.
27185      * @param {String} tooltip The tab's tooltip
27186      */
27187     setTooltip : function(text){
27188         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27189             this.textEl.dom.qtip = text;
27190             this.textEl.dom.removeAttribute('title');
27191         }else{
27192             this.textEl.dom.title = text;
27193         }
27194     },
27195
27196     onTabClick : function(e){
27197         e.preventDefault();
27198         this.tabPanel.activate(this.id);
27199     },
27200
27201     onTabMouseDown : function(e){
27202         e.preventDefault();
27203         this.tabPanel.activate(this.id);
27204     },
27205
27206     getWidth : function(){
27207         return this.inner.getWidth();
27208     },
27209
27210     setWidth : function(width){
27211         var iwidth = width - this.pnode.getPadding("lr");
27212         this.inner.setWidth(iwidth);
27213         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27214         this.pnode.setWidth(width);
27215     },
27216
27217     /**
27218      * Show or hide the tab
27219      * @param {Boolean} hidden True to hide or false to show.
27220      */
27221     setHidden : function(hidden){
27222         this.hidden = hidden;
27223         this.pnode.setStyle("display", hidden ? "none" : "");
27224     },
27225
27226     /**
27227      * Returns true if this tab is "hidden"
27228      * @return {Boolean}
27229      */
27230     isHidden : function(){
27231         return this.hidden;
27232     },
27233
27234     /**
27235      * Returns the text for this tab
27236      * @return {String}
27237      */
27238     getText : function(){
27239         return this.text;
27240     },
27241
27242     autoSize : function(){
27243         //this.el.beginMeasure();
27244         this.textEl.setWidth(1);
27245         /*
27246          *  #2804 [new] Tabs in Roojs
27247          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27248          */
27249         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27250         //this.el.endMeasure();
27251     },
27252
27253     /**
27254      * Sets the text for the tab (Note: this also sets the tooltip text)
27255      * @param {String} text The tab's text and tooltip
27256      */
27257     setText : function(text){
27258         this.text = text;
27259         this.textEl.update(text);
27260         this.setTooltip(text);
27261         if(!this.tabPanel.resizeTabs){
27262             this.autoSize();
27263         }
27264     },
27265     /**
27266      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27267      */
27268     activate : function(){
27269         this.tabPanel.activate(this.id);
27270     },
27271
27272     /**
27273      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27274      */
27275     disable : function(){
27276         if(this.tabPanel.active != this){
27277             this.disabled = true;
27278             this.pnode.addClass("disabled");
27279         }
27280     },
27281
27282     /**
27283      * Enables this TabPanelItem if it was previously disabled.
27284      */
27285     enable : function(){
27286         this.disabled = false;
27287         this.pnode.removeClass("disabled");
27288     },
27289
27290     /**
27291      * Sets the content for this TabPanelItem.
27292      * @param {String} content The content
27293      * @param {Boolean} loadScripts true to look for and load scripts
27294      */
27295     setContent : function(content, loadScripts){
27296         this.bodyEl.update(content, loadScripts);
27297     },
27298
27299     /**
27300      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27301      * @return {Roo.UpdateManager} The UpdateManager
27302      */
27303     getUpdateManager : function(){
27304         return this.bodyEl.getUpdateManager();
27305     },
27306
27307     /**
27308      * Set a URL to be used to load the content for this TabPanelItem.
27309      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27310      * @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)
27311      * @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)
27312      * @return {Roo.UpdateManager} The UpdateManager
27313      */
27314     setUrl : function(url, params, loadOnce){
27315         if(this.refreshDelegate){
27316             this.un('activate', this.refreshDelegate);
27317         }
27318         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27319         this.on("activate", this.refreshDelegate);
27320         return this.bodyEl.getUpdateManager();
27321     },
27322
27323     /** @private */
27324     _handleRefresh : function(url, params, loadOnce){
27325         if(!loadOnce || !this.loaded){
27326             var updater = this.bodyEl.getUpdateManager();
27327             updater.update(url, params, this._setLoaded.createDelegate(this));
27328         }
27329     },
27330
27331     /**
27332      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27333      *   Will fail silently if the setUrl method has not been called.
27334      *   This does not activate the panel, just updates its content.
27335      */
27336     refresh : function(){
27337         if(this.refreshDelegate){
27338            this.loaded = false;
27339            this.refreshDelegate();
27340         }
27341     },
27342
27343     /** @private */
27344     _setLoaded : function(){
27345         this.loaded = true;
27346     },
27347
27348     /** @private */
27349     closeClick : function(e){
27350         var o = {};
27351         e.stopEvent();
27352         this.fireEvent("beforeclose", this, o);
27353         if(o.cancel !== true){
27354             this.tabPanel.removeTab(this.id);
27355         }
27356     },
27357     /**
27358      * The text displayed in the tooltip for the close icon.
27359      * @type String
27360      */
27361     closeText : "Close this tab"
27362 });
27363
27364 /** @private */
27365 Roo.TabPanel.prototype.createStrip = function(container){
27366     var strip = document.createElement("div");
27367     strip.className = "x-tabs-wrap";
27368     container.appendChild(strip);
27369     return strip;
27370 };
27371 /** @private */
27372 Roo.TabPanel.prototype.createStripList = function(strip){
27373     // div wrapper for retard IE
27374     // returns the "tr" element.
27375     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27376         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27377         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27378     return strip.firstChild.firstChild.firstChild.firstChild;
27379 };
27380 /** @private */
27381 Roo.TabPanel.prototype.createBody = function(container){
27382     var body = document.createElement("div");
27383     Roo.id(body, "tab-body");
27384     Roo.fly(body).addClass("x-tabs-body");
27385     container.appendChild(body);
27386     return body;
27387 };
27388 /** @private */
27389 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27390     var body = Roo.getDom(id);
27391     if(!body){
27392         body = document.createElement("div");
27393         body.id = id;
27394     }
27395     Roo.fly(body).addClass("x-tabs-item-body");
27396     bodyEl.insertBefore(body, bodyEl.firstChild);
27397     return body;
27398 };
27399 /** @private */
27400 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27401     var td = document.createElement("td");
27402     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27403     //stripEl.appendChild(td);
27404     if(closable){
27405         td.className = "x-tabs-closable";
27406         if(!this.closeTpl){
27407             this.closeTpl = new Roo.Template(
27408                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27409                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27410                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27411             );
27412         }
27413         var el = this.closeTpl.overwrite(td, {"text": text});
27414         var close = el.getElementsByTagName("div")[0];
27415         var inner = el.getElementsByTagName("em")[0];
27416         return {"el": el, "close": close, "inner": inner};
27417     } else {
27418         if(!this.tabTpl){
27419             this.tabTpl = new Roo.Template(
27420                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27421                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27422             );
27423         }
27424         var el = this.tabTpl.overwrite(td, {"text": text});
27425         var inner = el.getElementsByTagName("em")[0];
27426         return {"el": el, "inner": inner};
27427     }
27428 };/*
27429  * Based on:
27430  * Ext JS Library 1.1.1
27431  * Copyright(c) 2006-2007, Ext JS, LLC.
27432  *
27433  * Originally Released Under LGPL - original licence link has changed is not relivant.
27434  *
27435  * Fork - LGPL
27436  * <script type="text/javascript">
27437  */
27438
27439 /**
27440  * @class Roo.Button
27441  * @extends Roo.util.Observable
27442  * Simple Button class
27443  * @cfg {String} text The button text
27444  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27445  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27446  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27447  * @cfg {Object} scope The scope of the handler
27448  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27449  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27450  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27451  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27452  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27453  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27454    applies if enableToggle = true)
27455  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27456  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27457   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27458  * @constructor
27459  * Create a new button
27460  * @param {Object} config The config object
27461  */
27462 Roo.Button = function(renderTo, config)
27463 {
27464     if (!config) {
27465         config = renderTo;
27466         renderTo = config.renderTo || false;
27467     }
27468     
27469     Roo.apply(this, config);
27470     this.addEvents({
27471         /**
27472              * @event click
27473              * Fires when this button is clicked
27474              * @param {Button} this
27475              * @param {EventObject} e The click event
27476              */
27477             "click" : true,
27478         /**
27479              * @event toggle
27480              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27481              * @param {Button} this
27482              * @param {Boolean} pressed
27483              */
27484             "toggle" : true,
27485         /**
27486              * @event mouseover
27487              * Fires when the mouse hovers over the button
27488              * @param {Button} this
27489              * @param {Event} e The event object
27490              */
27491         'mouseover' : true,
27492         /**
27493              * @event mouseout
27494              * Fires when the mouse exits the button
27495              * @param {Button} this
27496              * @param {Event} e The event object
27497              */
27498         'mouseout': true,
27499          /**
27500              * @event render
27501              * Fires when the button is rendered
27502              * @param {Button} this
27503              */
27504         'render': true
27505     });
27506     if(this.menu){
27507         this.menu = Roo.menu.MenuMgr.get(this.menu);
27508     }
27509     // register listeners first!!  - so render can be captured..
27510     Roo.util.Observable.call(this);
27511     if(renderTo){
27512         this.render(renderTo);
27513     }
27514     
27515   
27516 };
27517
27518 Roo.extend(Roo.Button, Roo.util.Observable, {
27519     /**
27520      * 
27521      */
27522     
27523     /**
27524      * Read-only. True if this button is hidden
27525      * @type Boolean
27526      */
27527     hidden : false,
27528     /**
27529      * Read-only. True if this button is disabled
27530      * @type Boolean
27531      */
27532     disabled : false,
27533     /**
27534      * Read-only. True if this button is pressed (only if enableToggle = true)
27535      * @type Boolean
27536      */
27537     pressed : false,
27538
27539     /**
27540      * @cfg {Number} tabIndex 
27541      * The DOM tabIndex for this button (defaults to undefined)
27542      */
27543     tabIndex : undefined,
27544
27545     /**
27546      * @cfg {Boolean} enableToggle
27547      * True to enable pressed/not pressed toggling (defaults to false)
27548      */
27549     enableToggle: false,
27550     /**
27551      * @cfg {Mixed} menu
27552      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27553      */
27554     menu : undefined,
27555     /**
27556      * @cfg {String} menuAlign
27557      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27558      */
27559     menuAlign : "tl-bl?",
27560
27561     /**
27562      * @cfg {String} iconCls
27563      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27564      */
27565     iconCls : undefined,
27566     /**
27567      * @cfg {String} type
27568      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27569      */
27570     type : 'button',
27571
27572     // private
27573     menuClassTarget: 'tr',
27574
27575     /**
27576      * @cfg {String} clickEvent
27577      * The type of event to map to the button's event handler (defaults to 'click')
27578      */
27579     clickEvent : 'click',
27580
27581     /**
27582      * @cfg {Boolean} handleMouseEvents
27583      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27584      */
27585     handleMouseEvents : true,
27586
27587     /**
27588      * @cfg {String} tooltipType
27589      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27590      */
27591     tooltipType : 'qtip',
27592
27593     /**
27594      * @cfg {String} cls
27595      * A CSS class to apply to the button's main element.
27596      */
27597     
27598     /**
27599      * @cfg {Roo.Template} template (Optional)
27600      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27601      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27602      * require code modifications if required elements (e.g. a button) aren't present.
27603      */
27604
27605     // private
27606     render : function(renderTo){
27607         var btn;
27608         if(this.hideParent){
27609             this.parentEl = Roo.get(renderTo);
27610         }
27611         if(!this.dhconfig){
27612             if(!this.template){
27613                 if(!Roo.Button.buttonTemplate){
27614                     // hideous table template
27615                     Roo.Button.buttonTemplate = new Roo.Template(
27616                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27617                         '<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>',
27618                         "</tr></tbody></table>");
27619                 }
27620                 this.template = Roo.Button.buttonTemplate;
27621             }
27622             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27623             var btnEl = btn.child("button:first");
27624             btnEl.on('focus', this.onFocus, this);
27625             btnEl.on('blur', this.onBlur, this);
27626             if(this.cls){
27627                 btn.addClass(this.cls);
27628             }
27629             if(this.icon){
27630                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27631             }
27632             if(this.iconCls){
27633                 btnEl.addClass(this.iconCls);
27634                 if(!this.cls){
27635                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27636                 }
27637             }
27638             if(this.tabIndex !== undefined){
27639                 btnEl.dom.tabIndex = this.tabIndex;
27640             }
27641             if(this.tooltip){
27642                 if(typeof this.tooltip == 'object'){
27643                     Roo.QuickTips.tips(Roo.apply({
27644                           target: btnEl.id
27645                     }, this.tooltip));
27646                 } else {
27647                     btnEl.dom[this.tooltipType] = this.tooltip;
27648                 }
27649             }
27650         }else{
27651             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27652         }
27653         this.el = btn;
27654         if(this.id){
27655             this.el.dom.id = this.el.id = this.id;
27656         }
27657         if(this.menu){
27658             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27659             this.menu.on("show", this.onMenuShow, this);
27660             this.menu.on("hide", this.onMenuHide, this);
27661         }
27662         btn.addClass("x-btn");
27663         if(Roo.isIE && !Roo.isIE7){
27664             this.autoWidth.defer(1, this);
27665         }else{
27666             this.autoWidth();
27667         }
27668         if(this.handleMouseEvents){
27669             btn.on("mouseover", this.onMouseOver, this);
27670             btn.on("mouseout", this.onMouseOut, this);
27671             btn.on("mousedown", this.onMouseDown, this);
27672         }
27673         btn.on(this.clickEvent, this.onClick, this);
27674         //btn.on("mouseup", this.onMouseUp, this);
27675         if(this.hidden){
27676             this.hide();
27677         }
27678         if(this.disabled){
27679             this.disable();
27680         }
27681         Roo.ButtonToggleMgr.register(this);
27682         if(this.pressed){
27683             this.el.addClass("x-btn-pressed");
27684         }
27685         if(this.repeat){
27686             var repeater = new Roo.util.ClickRepeater(btn,
27687                 typeof this.repeat == "object" ? this.repeat : {}
27688             );
27689             repeater.on("click", this.onClick,  this);
27690         }
27691         
27692         this.fireEvent('render', this);
27693         
27694     },
27695     /**
27696      * Returns the button's underlying element
27697      * @return {Roo.Element} The element
27698      */
27699     getEl : function(){
27700         return this.el;  
27701     },
27702     
27703     /**
27704      * Destroys this Button and removes any listeners.
27705      */
27706     destroy : function(){
27707         Roo.ButtonToggleMgr.unregister(this);
27708         this.el.removeAllListeners();
27709         this.purgeListeners();
27710         this.el.remove();
27711     },
27712
27713     // private
27714     autoWidth : function(){
27715         if(this.el){
27716             this.el.setWidth("auto");
27717             if(Roo.isIE7 && Roo.isStrict){
27718                 var ib = this.el.child('button');
27719                 if(ib && ib.getWidth() > 20){
27720                     ib.clip();
27721                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27722                 }
27723             }
27724             if(this.minWidth){
27725                 if(this.hidden){
27726                     this.el.beginMeasure();
27727                 }
27728                 if(this.el.getWidth() < this.minWidth){
27729                     this.el.setWidth(this.minWidth);
27730                 }
27731                 if(this.hidden){
27732                     this.el.endMeasure();
27733                 }
27734             }
27735         }
27736     },
27737
27738     /**
27739      * Assigns this button's click handler
27740      * @param {Function} handler The function to call when the button is clicked
27741      * @param {Object} scope (optional) Scope for the function passed in
27742      */
27743     setHandler : function(handler, scope){
27744         this.handler = handler;
27745         this.scope = scope;  
27746     },
27747     
27748     /**
27749      * Sets this button's text
27750      * @param {String} text The button text
27751      */
27752     setText : function(text){
27753         this.text = text;
27754         if(this.el){
27755             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27756         }
27757         this.autoWidth();
27758     },
27759     
27760     /**
27761      * Gets the text for this button
27762      * @return {String} The button text
27763      */
27764     getText : function(){
27765         return this.text;  
27766     },
27767     
27768     /**
27769      * Show this button
27770      */
27771     show: function(){
27772         this.hidden = false;
27773         if(this.el){
27774             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27775         }
27776     },
27777     
27778     /**
27779      * Hide this button
27780      */
27781     hide: function(){
27782         this.hidden = true;
27783         if(this.el){
27784             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27785         }
27786     },
27787     
27788     /**
27789      * Convenience function for boolean show/hide
27790      * @param {Boolean} visible True to show, false to hide
27791      */
27792     setVisible: function(visible){
27793         if(visible) {
27794             this.show();
27795         }else{
27796             this.hide();
27797         }
27798     },
27799     
27800     /**
27801      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27802      * @param {Boolean} state (optional) Force a particular state
27803      */
27804     toggle : function(state){
27805         state = state === undefined ? !this.pressed : state;
27806         if(state != this.pressed){
27807             if(state){
27808                 this.el.addClass("x-btn-pressed");
27809                 this.pressed = true;
27810                 this.fireEvent("toggle", this, true);
27811             }else{
27812                 this.el.removeClass("x-btn-pressed");
27813                 this.pressed = false;
27814                 this.fireEvent("toggle", this, false);
27815             }
27816             if(this.toggleHandler){
27817                 this.toggleHandler.call(this.scope || this, this, state);
27818             }
27819         }
27820     },
27821     
27822     /**
27823      * Focus the button
27824      */
27825     focus : function(){
27826         this.el.child('button:first').focus();
27827     },
27828     
27829     /**
27830      * Disable this button
27831      */
27832     disable : function(){
27833         if(this.el){
27834             this.el.addClass("x-btn-disabled");
27835         }
27836         this.disabled = true;
27837     },
27838     
27839     /**
27840      * Enable this button
27841      */
27842     enable : function(){
27843         if(this.el){
27844             this.el.removeClass("x-btn-disabled");
27845         }
27846         this.disabled = false;
27847     },
27848
27849     /**
27850      * Convenience function for boolean enable/disable
27851      * @param {Boolean} enabled True to enable, false to disable
27852      */
27853     setDisabled : function(v){
27854         this[v !== true ? "enable" : "disable"]();
27855     },
27856
27857     // private
27858     onClick : function(e){
27859         if(e){
27860             e.preventDefault();
27861         }
27862         if(e.button != 0){
27863             return;
27864         }
27865         if(!this.disabled){
27866             if(this.enableToggle){
27867                 this.toggle();
27868             }
27869             if(this.menu && !this.menu.isVisible()){
27870                 this.menu.show(this.el, this.menuAlign);
27871             }
27872             this.fireEvent("click", this, e);
27873             if(this.handler){
27874                 this.el.removeClass("x-btn-over");
27875                 this.handler.call(this.scope || this, this, e);
27876             }
27877         }
27878     },
27879     // private
27880     onMouseOver : function(e){
27881         if(!this.disabled){
27882             this.el.addClass("x-btn-over");
27883             this.fireEvent('mouseover', this, e);
27884         }
27885     },
27886     // private
27887     onMouseOut : function(e){
27888         if(!e.within(this.el,  true)){
27889             this.el.removeClass("x-btn-over");
27890             this.fireEvent('mouseout', this, e);
27891         }
27892     },
27893     // private
27894     onFocus : function(e){
27895         if(!this.disabled){
27896             this.el.addClass("x-btn-focus");
27897         }
27898     },
27899     // private
27900     onBlur : function(e){
27901         this.el.removeClass("x-btn-focus");
27902     },
27903     // private
27904     onMouseDown : function(e){
27905         if(!this.disabled && e.button == 0){
27906             this.el.addClass("x-btn-click");
27907             Roo.get(document).on('mouseup', this.onMouseUp, this);
27908         }
27909     },
27910     // private
27911     onMouseUp : function(e){
27912         if(e.button == 0){
27913             this.el.removeClass("x-btn-click");
27914             Roo.get(document).un('mouseup', this.onMouseUp, this);
27915         }
27916     },
27917     // private
27918     onMenuShow : function(e){
27919         this.el.addClass("x-btn-menu-active");
27920     },
27921     // private
27922     onMenuHide : function(e){
27923         this.el.removeClass("x-btn-menu-active");
27924     }   
27925 });
27926
27927 // Private utility class used by Button
27928 Roo.ButtonToggleMgr = function(){
27929    var groups = {};
27930    
27931    function toggleGroup(btn, state){
27932        if(state){
27933            var g = groups[btn.toggleGroup];
27934            for(var i = 0, l = g.length; i < l; i++){
27935                if(g[i] != btn){
27936                    g[i].toggle(false);
27937                }
27938            }
27939        }
27940    }
27941    
27942    return {
27943        register : function(btn){
27944            if(!btn.toggleGroup){
27945                return;
27946            }
27947            var g = groups[btn.toggleGroup];
27948            if(!g){
27949                g = groups[btn.toggleGroup] = [];
27950            }
27951            g.push(btn);
27952            btn.on("toggle", toggleGroup);
27953        },
27954        
27955        unregister : function(btn){
27956            if(!btn.toggleGroup){
27957                return;
27958            }
27959            var g = groups[btn.toggleGroup];
27960            if(g){
27961                g.remove(btn);
27962                btn.un("toggle", toggleGroup);
27963            }
27964        }
27965    };
27966 }();/*
27967  * Based on:
27968  * Ext JS Library 1.1.1
27969  * Copyright(c) 2006-2007, Ext JS, LLC.
27970  *
27971  * Originally Released Under LGPL - original licence link has changed is not relivant.
27972  *
27973  * Fork - LGPL
27974  * <script type="text/javascript">
27975  */
27976  
27977 /**
27978  * @class Roo.SplitButton
27979  * @extends Roo.Button
27980  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27981  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27982  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27983  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27984  * @cfg {String} arrowTooltip The title attribute of the arrow
27985  * @constructor
27986  * Create a new menu button
27987  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27988  * @param {Object} config The config object
27989  */
27990 Roo.SplitButton = function(renderTo, config){
27991     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27992     /**
27993      * @event arrowclick
27994      * Fires when this button's arrow is clicked
27995      * @param {SplitButton} this
27996      * @param {EventObject} e The click event
27997      */
27998     this.addEvents({"arrowclick":true});
27999 };
28000
28001 Roo.extend(Roo.SplitButton, Roo.Button, {
28002     render : function(renderTo){
28003         // this is one sweet looking template!
28004         var tpl = new Roo.Template(
28005             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28006             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28007             '<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>',
28008             "</tbody></table></td><td>",
28009             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28010             '<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>',
28011             "</tbody></table></td></tr></table>"
28012         );
28013         var btn = tpl.append(renderTo, [this.text, this.type], true);
28014         var btnEl = btn.child("button");
28015         if(this.cls){
28016             btn.addClass(this.cls);
28017         }
28018         if(this.icon){
28019             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28020         }
28021         if(this.iconCls){
28022             btnEl.addClass(this.iconCls);
28023             if(!this.cls){
28024                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28025             }
28026         }
28027         this.el = btn;
28028         if(this.handleMouseEvents){
28029             btn.on("mouseover", this.onMouseOver, this);
28030             btn.on("mouseout", this.onMouseOut, this);
28031             btn.on("mousedown", this.onMouseDown, this);
28032             btn.on("mouseup", this.onMouseUp, this);
28033         }
28034         btn.on(this.clickEvent, this.onClick, this);
28035         if(this.tooltip){
28036             if(typeof this.tooltip == 'object'){
28037                 Roo.QuickTips.tips(Roo.apply({
28038                       target: btnEl.id
28039                 }, this.tooltip));
28040             } else {
28041                 btnEl.dom[this.tooltipType] = this.tooltip;
28042             }
28043         }
28044         if(this.arrowTooltip){
28045             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28046         }
28047         if(this.hidden){
28048             this.hide();
28049         }
28050         if(this.disabled){
28051             this.disable();
28052         }
28053         if(this.pressed){
28054             this.el.addClass("x-btn-pressed");
28055         }
28056         if(Roo.isIE && !Roo.isIE7){
28057             this.autoWidth.defer(1, this);
28058         }else{
28059             this.autoWidth();
28060         }
28061         if(this.menu){
28062             this.menu.on("show", this.onMenuShow, this);
28063             this.menu.on("hide", this.onMenuHide, this);
28064         }
28065         this.fireEvent('render', this);
28066     },
28067
28068     // private
28069     autoWidth : function(){
28070         if(this.el){
28071             var tbl = this.el.child("table:first");
28072             var tbl2 = this.el.child("table:last");
28073             this.el.setWidth("auto");
28074             tbl.setWidth("auto");
28075             if(Roo.isIE7 && Roo.isStrict){
28076                 var ib = this.el.child('button:first');
28077                 if(ib && ib.getWidth() > 20){
28078                     ib.clip();
28079                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28080                 }
28081             }
28082             if(this.minWidth){
28083                 if(this.hidden){
28084                     this.el.beginMeasure();
28085                 }
28086                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28087                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28088                 }
28089                 if(this.hidden){
28090                     this.el.endMeasure();
28091                 }
28092             }
28093             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28094         } 
28095     },
28096     /**
28097      * Sets this button's click handler
28098      * @param {Function} handler The function to call when the button is clicked
28099      * @param {Object} scope (optional) Scope for the function passed above
28100      */
28101     setHandler : function(handler, scope){
28102         this.handler = handler;
28103         this.scope = scope;  
28104     },
28105     
28106     /**
28107      * Sets this button's arrow click handler
28108      * @param {Function} handler The function to call when the arrow is clicked
28109      * @param {Object} scope (optional) Scope for the function passed above
28110      */
28111     setArrowHandler : function(handler, scope){
28112         this.arrowHandler = handler;
28113         this.scope = scope;  
28114     },
28115     
28116     /**
28117      * Focus the button
28118      */
28119     focus : function(){
28120         if(this.el){
28121             this.el.child("button:first").focus();
28122         }
28123     },
28124
28125     // private
28126     onClick : function(e){
28127         e.preventDefault();
28128         if(!this.disabled){
28129             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28130                 if(this.menu && !this.menu.isVisible()){
28131                     this.menu.show(this.el, this.menuAlign);
28132                 }
28133                 this.fireEvent("arrowclick", this, e);
28134                 if(this.arrowHandler){
28135                     this.arrowHandler.call(this.scope || this, this, e);
28136                 }
28137             }else{
28138                 this.fireEvent("click", this, e);
28139                 if(this.handler){
28140                     this.handler.call(this.scope || this, this, e);
28141                 }
28142             }
28143         }
28144     },
28145     // private
28146     onMouseDown : function(e){
28147         if(!this.disabled){
28148             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28149         }
28150     },
28151     // private
28152     onMouseUp : function(e){
28153         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28154     }   
28155 });
28156
28157
28158 // backwards compat
28159 Roo.MenuButton = Roo.SplitButton;/*
28160  * Based on:
28161  * Ext JS Library 1.1.1
28162  * Copyright(c) 2006-2007, Ext JS, LLC.
28163  *
28164  * Originally Released Under LGPL - original licence link has changed is not relivant.
28165  *
28166  * Fork - LGPL
28167  * <script type="text/javascript">
28168  */
28169
28170 /**
28171  * @class Roo.Toolbar
28172  * Basic Toolbar class.
28173  * @constructor
28174  * Creates a new Toolbar
28175  * @param {Object} container The config object
28176  */ 
28177 Roo.Toolbar = function(container, buttons, config)
28178 {
28179     /// old consturctor format still supported..
28180     if(container instanceof Array){ // omit the container for later rendering
28181         buttons = container;
28182         config = buttons;
28183         container = null;
28184     }
28185     if (typeof(container) == 'object' && container.xtype) {
28186         config = container;
28187         container = config.container;
28188         buttons = config.buttons || []; // not really - use items!!
28189     }
28190     var xitems = [];
28191     if (config && config.items) {
28192         xitems = config.items;
28193         delete config.items;
28194     }
28195     Roo.apply(this, config);
28196     this.buttons = buttons;
28197     
28198     if(container){
28199         this.render(container);
28200     }
28201     this.xitems = xitems;
28202     Roo.each(xitems, function(b) {
28203         this.add(b);
28204     }, this);
28205     
28206 };
28207
28208 Roo.Toolbar.prototype = {
28209     /**
28210      * @cfg {Array} items
28211      * array of button configs or elements to add (will be converted to a MixedCollection)
28212      */
28213     
28214     /**
28215      * @cfg {String/HTMLElement/Element} container
28216      * The id or element that will contain the toolbar
28217      */
28218     // private
28219     render : function(ct){
28220         this.el = Roo.get(ct);
28221         if(this.cls){
28222             this.el.addClass(this.cls);
28223         }
28224         // using a table allows for vertical alignment
28225         // 100% width is needed by Safari...
28226         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28227         this.tr = this.el.child("tr", true);
28228         var autoId = 0;
28229         this.items = new Roo.util.MixedCollection(false, function(o){
28230             return o.id || ("item" + (++autoId));
28231         });
28232         if(this.buttons){
28233             this.add.apply(this, this.buttons);
28234             delete this.buttons;
28235         }
28236     },
28237
28238     /**
28239      * Adds element(s) to the toolbar -- this function takes a variable number of 
28240      * arguments of mixed type and adds them to the toolbar.
28241      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28242      * <ul>
28243      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28244      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28245      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28246      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28247      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28248      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28249      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28250      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28251      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28252      * </ul>
28253      * @param {Mixed} arg2
28254      * @param {Mixed} etc.
28255      */
28256     add : function(){
28257         var a = arguments, l = a.length;
28258         for(var i = 0; i < l; i++){
28259             this._add(a[i]);
28260         }
28261     },
28262     // private..
28263     _add : function(el) {
28264         
28265         if (el.xtype) {
28266             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28267         }
28268         
28269         if (el.applyTo){ // some kind of form field
28270             return this.addField(el);
28271         } 
28272         if (el.render){ // some kind of Toolbar.Item
28273             return this.addItem(el);
28274         }
28275         if (typeof el == "string"){ // string
28276             if(el == "separator" || el == "-"){
28277                 return this.addSeparator();
28278             }
28279             if (el == " "){
28280                 return this.addSpacer();
28281             }
28282             if(el == "->"){
28283                 return this.addFill();
28284             }
28285             return this.addText(el);
28286             
28287         }
28288         if(el.tagName){ // element
28289             return this.addElement(el);
28290         }
28291         if(typeof el == "object"){ // must be button config?
28292             return this.addButton(el);
28293         }
28294         // and now what?!?!
28295         return false;
28296         
28297     },
28298     
28299     /**
28300      * Add an Xtype element
28301      * @param {Object} xtype Xtype Object
28302      * @return {Object} created Object
28303      */
28304     addxtype : function(e){
28305         return this.add(e);  
28306     },
28307     
28308     /**
28309      * Returns the Element for this toolbar.
28310      * @return {Roo.Element}
28311      */
28312     getEl : function(){
28313         return this.el;  
28314     },
28315     
28316     /**
28317      * Adds a separator
28318      * @return {Roo.Toolbar.Item} The separator item
28319      */
28320     addSeparator : function(){
28321         return this.addItem(new Roo.Toolbar.Separator());
28322     },
28323
28324     /**
28325      * Adds a spacer element
28326      * @return {Roo.Toolbar.Spacer} The spacer item
28327      */
28328     addSpacer : function(){
28329         return this.addItem(new Roo.Toolbar.Spacer());
28330     },
28331
28332     /**
28333      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28334      * @return {Roo.Toolbar.Fill} The fill item
28335      */
28336     addFill : function(){
28337         return this.addItem(new Roo.Toolbar.Fill());
28338     },
28339
28340     /**
28341      * Adds any standard HTML element to the toolbar
28342      * @param {String/HTMLElement/Element} el The element or id of the element to add
28343      * @return {Roo.Toolbar.Item} The element's item
28344      */
28345     addElement : function(el){
28346         return this.addItem(new Roo.Toolbar.Item(el));
28347     },
28348     /**
28349      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28350      * @type Roo.util.MixedCollection  
28351      */
28352     items : false,
28353      
28354     /**
28355      * Adds any Toolbar.Item or subclass
28356      * @param {Roo.Toolbar.Item} item
28357      * @return {Roo.Toolbar.Item} The item
28358      */
28359     addItem : function(item){
28360         var td = this.nextBlock();
28361         item.render(td);
28362         this.items.add(item);
28363         return item;
28364     },
28365     
28366     /**
28367      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28368      * @param {Object/Array} config A button config or array of configs
28369      * @return {Roo.Toolbar.Button/Array}
28370      */
28371     addButton : function(config){
28372         if(config instanceof Array){
28373             var buttons = [];
28374             for(var i = 0, len = config.length; i < len; i++) {
28375                 buttons.push(this.addButton(config[i]));
28376             }
28377             return buttons;
28378         }
28379         var b = config;
28380         if(!(config instanceof Roo.Toolbar.Button)){
28381             b = config.split ?
28382                 new Roo.Toolbar.SplitButton(config) :
28383                 new Roo.Toolbar.Button(config);
28384         }
28385         var td = this.nextBlock();
28386         b.render(td);
28387         this.items.add(b);
28388         return b;
28389     },
28390     
28391     /**
28392      * Adds text to the toolbar
28393      * @param {String} text The text to add
28394      * @return {Roo.Toolbar.Item} The element's item
28395      */
28396     addText : function(text){
28397         return this.addItem(new Roo.Toolbar.TextItem(text));
28398     },
28399     
28400     /**
28401      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28402      * @param {Number} index The index where the item is to be inserted
28403      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28404      * @return {Roo.Toolbar.Button/Item}
28405      */
28406     insertButton : function(index, item){
28407         if(item instanceof Array){
28408             var buttons = [];
28409             for(var i = 0, len = item.length; i < len; i++) {
28410                buttons.push(this.insertButton(index + i, item[i]));
28411             }
28412             return buttons;
28413         }
28414         if (!(item instanceof Roo.Toolbar.Button)){
28415            item = new Roo.Toolbar.Button(item);
28416         }
28417         var td = document.createElement("td");
28418         this.tr.insertBefore(td, this.tr.childNodes[index]);
28419         item.render(td);
28420         this.items.insert(index, item);
28421         return item;
28422     },
28423     
28424     /**
28425      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28426      * @param {Object} config
28427      * @return {Roo.Toolbar.Item} The element's item
28428      */
28429     addDom : function(config, returnEl){
28430         var td = this.nextBlock();
28431         Roo.DomHelper.overwrite(td, config);
28432         var ti = new Roo.Toolbar.Item(td.firstChild);
28433         ti.render(td);
28434         this.items.add(ti);
28435         return ti;
28436     },
28437
28438     /**
28439      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28440      * @type Roo.util.MixedCollection  
28441      */
28442     fields : false,
28443     
28444     /**
28445      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28446      * Note: the field should not have been rendered yet. For a field that has already been
28447      * rendered, use {@link #addElement}.
28448      * @param {Roo.form.Field} field
28449      * @return {Roo.ToolbarItem}
28450      */
28451      
28452       
28453     addField : function(field) {
28454         if (!this.fields) {
28455             var autoId = 0;
28456             this.fields = new Roo.util.MixedCollection(false, function(o){
28457                 return o.id || ("item" + (++autoId));
28458             });
28459
28460         }
28461         
28462         var td = this.nextBlock();
28463         field.render(td);
28464         var ti = new Roo.Toolbar.Item(td.firstChild);
28465         ti.render(td);
28466         this.items.add(ti);
28467         this.fields.add(field);
28468         return ti;
28469     },
28470     /**
28471      * Hide the toolbar
28472      * @method hide
28473      */
28474      
28475       
28476     hide : function()
28477     {
28478         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28479         this.el.child('div').hide();
28480     },
28481     /**
28482      * Show the toolbar
28483      * @method show
28484      */
28485     show : function()
28486     {
28487         this.el.child('div').show();
28488     },
28489       
28490     // private
28491     nextBlock : function(){
28492         var td = document.createElement("td");
28493         this.tr.appendChild(td);
28494         return td;
28495     },
28496
28497     // private
28498     destroy : function(){
28499         if(this.items){ // rendered?
28500             Roo.destroy.apply(Roo, this.items.items);
28501         }
28502         if(this.fields){ // rendered?
28503             Roo.destroy.apply(Roo, this.fields.items);
28504         }
28505         Roo.Element.uncache(this.el, this.tr);
28506     }
28507 };
28508
28509 /**
28510  * @class Roo.Toolbar.Item
28511  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28512  * @constructor
28513  * Creates a new Item
28514  * @param {HTMLElement} el 
28515  */
28516 Roo.Toolbar.Item = function(el){
28517     this.el = Roo.getDom(el);
28518     this.id = Roo.id(this.el);
28519     this.hidden = false;
28520 };
28521
28522 Roo.Toolbar.Item.prototype = {
28523     
28524     /**
28525      * Get this item's HTML Element
28526      * @return {HTMLElement}
28527      */
28528     getEl : function(){
28529        return this.el;  
28530     },
28531
28532     // private
28533     render : function(td){
28534         this.td = td;
28535         td.appendChild(this.el);
28536     },
28537     
28538     /**
28539      * Removes and destroys this item.
28540      */
28541     destroy : function(){
28542         this.td.parentNode.removeChild(this.td);
28543     },
28544     
28545     /**
28546      * Shows this item.
28547      */
28548     show: function(){
28549         this.hidden = false;
28550         this.td.style.display = "";
28551     },
28552     
28553     /**
28554      * Hides this item.
28555      */
28556     hide: function(){
28557         this.hidden = true;
28558         this.td.style.display = "none";
28559     },
28560     
28561     /**
28562      * Convenience function for boolean show/hide.
28563      * @param {Boolean} visible true to show/false to hide
28564      */
28565     setVisible: function(visible){
28566         if(visible) {
28567             this.show();
28568         }else{
28569             this.hide();
28570         }
28571     },
28572     
28573     /**
28574      * Try to focus this item.
28575      */
28576     focus : function(){
28577         Roo.fly(this.el).focus();
28578     },
28579     
28580     /**
28581      * Disables this item.
28582      */
28583     disable : function(){
28584         Roo.fly(this.td).addClass("x-item-disabled");
28585         this.disabled = true;
28586         this.el.disabled = true;
28587     },
28588     
28589     /**
28590      * Enables this item.
28591      */
28592     enable : function(){
28593         Roo.fly(this.td).removeClass("x-item-disabled");
28594         this.disabled = false;
28595         this.el.disabled = false;
28596     }
28597 };
28598
28599
28600 /**
28601  * @class Roo.Toolbar.Separator
28602  * @extends Roo.Toolbar.Item
28603  * A simple toolbar separator class
28604  * @constructor
28605  * Creates a new Separator
28606  */
28607 Roo.Toolbar.Separator = function(){
28608     var s = document.createElement("span");
28609     s.className = "ytb-sep";
28610     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28611 };
28612 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28613     enable:Roo.emptyFn,
28614     disable:Roo.emptyFn,
28615     focus:Roo.emptyFn
28616 });
28617
28618 /**
28619  * @class Roo.Toolbar.Spacer
28620  * @extends Roo.Toolbar.Item
28621  * A simple element that adds extra horizontal space to a toolbar.
28622  * @constructor
28623  * Creates a new Spacer
28624  */
28625 Roo.Toolbar.Spacer = function(){
28626     var s = document.createElement("div");
28627     s.className = "ytb-spacer";
28628     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28629 };
28630 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28631     enable:Roo.emptyFn,
28632     disable:Roo.emptyFn,
28633     focus:Roo.emptyFn
28634 });
28635
28636 /**
28637  * @class Roo.Toolbar.Fill
28638  * @extends Roo.Toolbar.Spacer
28639  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28640  * @constructor
28641  * Creates a new Spacer
28642  */
28643 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28644     // private
28645     render : function(td){
28646         td.style.width = '100%';
28647         Roo.Toolbar.Fill.superclass.render.call(this, td);
28648     }
28649 });
28650
28651 /**
28652  * @class Roo.Toolbar.TextItem
28653  * @extends Roo.Toolbar.Item
28654  * A simple class that renders text directly into a toolbar.
28655  * @constructor
28656  * Creates a new TextItem
28657  * @param {String} text
28658  */
28659 Roo.Toolbar.TextItem = function(text){
28660     if (typeof(text) == 'object') {
28661         text = text.text;
28662     }
28663     var s = document.createElement("span");
28664     s.className = "ytb-text";
28665     s.innerHTML = text;
28666     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28667 };
28668 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28669     enable:Roo.emptyFn,
28670     disable:Roo.emptyFn,
28671     focus:Roo.emptyFn
28672 });
28673
28674 /**
28675  * @class Roo.Toolbar.Button
28676  * @extends Roo.Button
28677  * A button that renders into a toolbar.
28678  * @constructor
28679  * Creates a new Button
28680  * @param {Object} config A standard {@link Roo.Button} config object
28681  */
28682 Roo.Toolbar.Button = function(config){
28683     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28684 };
28685 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28686     render : function(td){
28687         this.td = td;
28688         Roo.Toolbar.Button.superclass.render.call(this, td);
28689     },
28690     
28691     /**
28692      * Removes and destroys this button
28693      */
28694     destroy : function(){
28695         Roo.Toolbar.Button.superclass.destroy.call(this);
28696         this.td.parentNode.removeChild(this.td);
28697     },
28698     
28699     /**
28700      * Shows this button
28701      */
28702     show: function(){
28703         this.hidden = false;
28704         this.td.style.display = "";
28705     },
28706     
28707     /**
28708      * Hides this button
28709      */
28710     hide: function(){
28711         this.hidden = true;
28712         this.td.style.display = "none";
28713     },
28714
28715     /**
28716      * Disables this item
28717      */
28718     disable : function(){
28719         Roo.fly(this.td).addClass("x-item-disabled");
28720         this.disabled = true;
28721     },
28722
28723     /**
28724      * Enables this item
28725      */
28726     enable : function(){
28727         Roo.fly(this.td).removeClass("x-item-disabled");
28728         this.disabled = false;
28729     }
28730 });
28731 // backwards compat
28732 Roo.ToolbarButton = Roo.Toolbar.Button;
28733
28734 /**
28735  * @class Roo.Toolbar.SplitButton
28736  * @extends Roo.SplitButton
28737  * A menu button that renders into a toolbar.
28738  * @constructor
28739  * Creates a new SplitButton
28740  * @param {Object} config A standard {@link Roo.SplitButton} config object
28741  */
28742 Roo.Toolbar.SplitButton = function(config){
28743     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28744 };
28745 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28746     render : function(td){
28747         this.td = td;
28748         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28749     },
28750     
28751     /**
28752      * Removes and destroys this button
28753      */
28754     destroy : function(){
28755         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28756         this.td.parentNode.removeChild(this.td);
28757     },
28758     
28759     /**
28760      * Shows this button
28761      */
28762     show: function(){
28763         this.hidden = false;
28764         this.td.style.display = "";
28765     },
28766     
28767     /**
28768      * Hides this button
28769      */
28770     hide: function(){
28771         this.hidden = true;
28772         this.td.style.display = "none";
28773     }
28774 });
28775
28776 // backwards compat
28777 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28778  * Based on:
28779  * Ext JS Library 1.1.1
28780  * Copyright(c) 2006-2007, Ext JS, LLC.
28781  *
28782  * Originally Released Under LGPL - original licence link has changed is not relivant.
28783  *
28784  * Fork - LGPL
28785  * <script type="text/javascript">
28786  */
28787  
28788 /**
28789  * @class Roo.PagingToolbar
28790  * @extends Roo.Toolbar
28791  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28792  * @constructor
28793  * Create a new PagingToolbar
28794  * @param {Object} config The config object
28795  */
28796 Roo.PagingToolbar = function(el, ds, config)
28797 {
28798     // old args format still supported... - xtype is prefered..
28799     if (typeof(el) == 'object' && el.xtype) {
28800         // created from xtype...
28801         config = el;
28802         ds = el.dataSource;
28803         el = config.container;
28804     }
28805     var items = [];
28806     if (config.items) {
28807         items = config.items;
28808         config.items = [];
28809     }
28810     
28811     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28812     this.ds = ds;
28813     this.cursor = 0;
28814     this.renderButtons(this.el);
28815     this.bind(ds);
28816     
28817     // supprot items array.
28818    
28819     Roo.each(items, function(e) {
28820         this.add(Roo.factory(e));
28821     },this);
28822     
28823 };
28824
28825 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28826     /**
28827      * @cfg {Roo.data.Store} dataSource
28828      * The underlying data store providing the paged data
28829      */
28830     /**
28831      * @cfg {String/HTMLElement/Element} container
28832      * container The id or element that will contain the toolbar
28833      */
28834     /**
28835      * @cfg {Boolean} displayInfo
28836      * True to display the displayMsg (defaults to false)
28837      */
28838     /**
28839      * @cfg {Number} pageSize
28840      * The number of records to display per page (defaults to 20)
28841      */
28842     pageSize: 20,
28843     /**
28844      * @cfg {String} displayMsg
28845      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28846      */
28847     displayMsg : 'Displaying {0} - {1} of {2}',
28848     /**
28849      * @cfg {String} emptyMsg
28850      * The message to display when no records are found (defaults to "No data to display")
28851      */
28852     emptyMsg : 'No data to display',
28853     /**
28854      * Customizable piece of the default paging text (defaults to "Page")
28855      * @type String
28856      */
28857     beforePageText : "Page",
28858     /**
28859      * Customizable piece of the default paging text (defaults to "of %0")
28860      * @type String
28861      */
28862     afterPageText : "of {0}",
28863     /**
28864      * Customizable piece of the default paging text (defaults to "First Page")
28865      * @type String
28866      */
28867     firstText : "First Page",
28868     /**
28869      * Customizable piece of the default paging text (defaults to "Previous Page")
28870      * @type String
28871      */
28872     prevText : "Previous Page",
28873     /**
28874      * Customizable piece of the default paging text (defaults to "Next Page")
28875      * @type String
28876      */
28877     nextText : "Next Page",
28878     /**
28879      * Customizable piece of the default paging text (defaults to "Last Page")
28880      * @type String
28881      */
28882     lastText : "Last Page",
28883     /**
28884      * Customizable piece of the default paging text (defaults to "Refresh")
28885      * @type String
28886      */
28887     refreshText : "Refresh",
28888
28889     // private
28890     renderButtons : function(el){
28891         Roo.PagingToolbar.superclass.render.call(this, el);
28892         this.first = this.addButton({
28893             tooltip: this.firstText,
28894             cls: "x-btn-icon x-grid-page-first",
28895             disabled: true,
28896             handler: this.onClick.createDelegate(this, ["first"])
28897         });
28898         this.prev = this.addButton({
28899             tooltip: this.prevText,
28900             cls: "x-btn-icon x-grid-page-prev",
28901             disabled: true,
28902             handler: this.onClick.createDelegate(this, ["prev"])
28903         });
28904         //this.addSeparator();
28905         this.add(this.beforePageText);
28906         this.field = Roo.get(this.addDom({
28907            tag: "input",
28908            type: "text",
28909            size: "3",
28910            value: "1",
28911            cls: "x-grid-page-number"
28912         }).el);
28913         this.field.on("keydown", this.onPagingKeydown, this);
28914         this.field.on("focus", function(){this.dom.select();});
28915         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28916         this.field.setHeight(18);
28917         //this.addSeparator();
28918         this.next = this.addButton({
28919             tooltip: this.nextText,
28920             cls: "x-btn-icon x-grid-page-next",
28921             disabled: true,
28922             handler: this.onClick.createDelegate(this, ["next"])
28923         });
28924         this.last = this.addButton({
28925             tooltip: this.lastText,
28926             cls: "x-btn-icon x-grid-page-last",
28927             disabled: true,
28928             handler: this.onClick.createDelegate(this, ["last"])
28929         });
28930         //this.addSeparator();
28931         this.loading = this.addButton({
28932             tooltip: this.refreshText,
28933             cls: "x-btn-icon x-grid-loading",
28934             handler: this.onClick.createDelegate(this, ["refresh"])
28935         });
28936
28937         if(this.displayInfo){
28938             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28939         }
28940     },
28941
28942     // private
28943     updateInfo : function(){
28944         if(this.displayEl){
28945             var count = this.ds.getCount();
28946             var msg = count == 0 ?
28947                 this.emptyMsg :
28948                 String.format(
28949                     this.displayMsg,
28950                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28951                 );
28952             this.displayEl.update(msg);
28953         }
28954     },
28955
28956     // private
28957     onLoad : function(ds, r, o){
28958        this.cursor = o.params ? o.params.start : 0;
28959        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28960
28961        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28962        this.field.dom.value = ap;
28963        this.first.setDisabled(ap == 1);
28964        this.prev.setDisabled(ap == 1);
28965        this.next.setDisabled(ap == ps);
28966        this.last.setDisabled(ap == ps);
28967        this.loading.enable();
28968        this.updateInfo();
28969     },
28970
28971     // private
28972     getPageData : function(){
28973         var total = this.ds.getTotalCount();
28974         return {
28975             total : total,
28976             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28977             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28978         };
28979     },
28980
28981     // private
28982     onLoadError : function(){
28983         this.loading.enable();
28984     },
28985
28986     // private
28987     onPagingKeydown : function(e){
28988         var k = e.getKey();
28989         var d = this.getPageData();
28990         if(k == e.RETURN){
28991             var v = this.field.dom.value, pageNum;
28992             if(!v || isNaN(pageNum = parseInt(v, 10))){
28993                 this.field.dom.value = d.activePage;
28994                 return;
28995             }
28996             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28997             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28998             e.stopEvent();
28999         }
29000         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))
29001         {
29002           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29003           this.field.dom.value = pageNum;
29004           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29005           e.stopEvent();
29006         }
29007         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29008         {
29009           var v = this.field.dom.value, pageNum; 
29010           var increment = (e.shiftKey) ? 10 : 1;
29011           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29012             increment *= -1;
29013           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29014             this.field.dom.value = d.activePage;
29015             return;
29016           }
29017           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29018           {
29019             this.field.dom.value = parseInt(v, 10) + increment;
29020             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29021             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29022           }
29023           e.stopEvent();
29024         }
29025     },
29026
29027     // private
29028     beforeLoad : function(){
29029         if(this.loading){
29030             this.loading.disable();
29031         }
29032     },
29033
29034     // private
29035     onClick : function(which){
29036         var ds = this.ds;
29037         switch(which){
29038             case "first":
29039                 ds.load({params:{start: 0, limit: this.pageSize}});
29040             break;
29041             case "prev":
29042                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29043             break;
29044             case "next":
29045                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29046             break;
29047             case "last":
29048                 var total = ds.getTotalCount();
29049                 var extra = total % this.pageSize;
29050                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29051                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29052             break;
29053             case "refresh":
29054                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29055             break;
29056         }
29057     },
29058
29059     /**
29060      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29061      * @param {Roo.data.Store} store The data store to unbind
29062      */
29063     unbind : function(ds){
29064         ds.un("beforeload", this.beforeLoad, this);
29065         ds.un("load", this.onLoad, this);
29066         ds.un("loadexception", this.onLoadError, this);
29067         ds.un("remove", this.updateInfo, this);
29068         ds.un("add", this.updateInfo, this);
29069         this.ds = undefined;
29070     },
29071
29072     /**
29073      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29074      * @param {Roo.data.Store} store The data store to bind
29075      */
29076     bind : function(ds){
29077         ds.on("beforeload", this.beforeLoad, this);
29078         ds.on("load", this.onLoad, this);
29079         ds.on("loadexception", this.onLoadError, this);
29080         ds.on("remove", this.updateInfo, this);
29081         ds.on("add", this.updateInfo, this);
29082         this.ds = ds;
29083     }
29084 });/*
29085  * Based on:
29086  * Ext JS Library 1.1.1
29087  * Copyright(c) 2006-2007, Ext JS, LLC.
29088  *
29089  * Originally Released Under LGPL - original licence link has changed is not relivant.
29090  *
29091  * Fork - LGPL
29092  * <script type="text/javascript">
29093  */
29094
29095 /**
29096  * @class Roo.Resizable
29097  * @extends Roo.util.Observable
29098  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29099  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29100  * 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
29101  * the element will be wrapped for you automatically.</p>
29102  * <p>Here is the list of valid resize handles:</p>
29103  * <pre>
29104 Value   Description
29105 ------  -------------------
29106  'n'     north
29107  's'     south
29108  'e'     east
29109  'w'     west
29110  'nw'    northwest
29111  'sw'    southwest
29112  'se'    southeast
29113  'ne'    northeast
29114  'hd'    horizontal drag
29115  'all'   all
29116 </pre>
29117  * <p>Here's an example showing the creation of a typical Resizable:</p>
29118  * <pre><code>
29119 var resizer = new Roo.Resizable("element-id", {
29120     handles: 'all',
29121     minWidth: 200,
29122     minHeight: 100,
29123     maxWidth: 500,
29124     maxHeight: 400,
29125     pinned: true
29126 });
29127 resizer.on("resize", myHandler);
29128 </code></pre>
29129  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29130  * resizer.east.setDisplayed(false);</p>
29131  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29132  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29133  * resize operation's new size (defaults to [0, 0])
29134  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29135  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29136  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29137  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29138  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29139  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29140  * @cfg {Number} width The width of the element in pixels (defaults to null)
29141  * @cfg {Number} height The height of the element in pixels (defaults to null)
29142  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29143  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29144  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29145  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29146  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29147  * in favor of the handles config option (defaults to false)
29148  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29149  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29150  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29151  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29152  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29153  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29154  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29155  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29156  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29157  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29158  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29159  * @constructor
29160  * Create a new resizable component
29161  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29162  * @param {Object} config configuration options
29163   */
29164 Roo.Resizable = function(el, config)
29165 {
29166     this.el = Roo.get(el);
29167
29168     if(config && config.wrap){
29169         config.resizeChild = this.el;
29170         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29171         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29172         this.el.setStyle("overflow", "hidden");
29173         this.el.setPositioning(config.resizeChild.getPositioning());
29174         config.resizeChild.clearPositioning();
29175         if(!config.width || !config.height){
29176             var csize = config.resizeChild.getSize();
29177             this.el.setSize(csize.width, csize.height);
29178         }
29179         if(config.pinned && !config.adjustments){
29180             config.adjustments = "auto";
29181         }
29182     }
29183
29184     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29185     this.proxy.unselectable();
29186     this.proxy.enableDisplayMode('block');
29187
29188     Roo.apply(this, config);
29189
29190     if(this.pinned){
29191         this.disableTrackOver = true;
29192         this.el.addClass("x-resizable-pinned");
29193     }
29194     // if the element isn't positioned, make it relative
29195     var position = this.el.getStyle("position");
29196     if(position != "absolute" && position != "fixed"){
29197         this.el.setStyle("position", "relative");
29198     }
29199     if(!this.handles){ // no handles passed, must be legacy style
29200         this.handles = 's,e,se';
29201         if(this.multiDirectional){
29202             this.handles += ',n,w';
29203         }
29204     }
29205     if(this.handles == "all"){
29206         this.handles = "n s e w ne nw se sw";
29207     }
29208     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29209     var ps = Roo.Resizable.positions;
29210     for(var i = 0, len = hs.length; i < len; i++){
29211         if(hs[i] && ps[hs[i]]){
29212             var pos = ps[hs[i]];
29213             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29214         }
29215     }
29216     // legacy
29217     this.corner = this.southeast;
29218     
29219     // updateBox = the box can move..
29220     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29221         this.updateBox = true;
29222     }
29223
29224     this.activeHandle = null;
29225
29226     if(this.resizeChild){
29227         if(typeof this.resizeChild == "boolean"){
29228             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29229         }else{
29230             this.resizeChild = Roo.get(this.resizeChild, true);
29231         }
29232     }
29233     
29234     if(this.adjustments == "auto"){
29235         var rc = this.resizeChild;
29236         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29237         if(rc && (hw || hn)){
29238             rc.position("relative");
29239             rc.setLeft(hw ? hw.el.getWidth() : 0);
29240             rc.setTop(hn ? hn.el.getHeight() : 0);
29241         }
29242         this.adjustments = [
29243             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29244             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29245         ];
29246     }
29247
29248     if(this.draggable){
29249         this.dd = this.dynamic ?
29250             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29251         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29252     }
29253
29254     // public events
29255     this.addEvents({
29256         /**
29257          * @event beforeresize
29258          * Fired before resize is allowed. Set enabled to false to cancel resize.
29259          * @param {Roo.Resizable} this
29260          * @param {Roo.EventObject} e The mousedown event
29261          */
29262         "beforeresize" : true,
29263         /**
29264          * @event resizing
29265          * Fired a resizing.
29266          * @param {Roo.Resizable} this
29267          * @param {Number} x The new x position
29268          * @param {Number} y The new y position
29269          * @param {Number} w The new w width
29270          * @param {Number} h The new h hight
29271          * @param {Roo.EventObject} e The mouseup event
29272          */
29273         "resizing" : true,
29274         /**
29275          * @event resize
29276          * Fired after a resize.
29277          * @param {Roo.Resizable} this
29278          * @param {Number} width The new width
29279          * @param {Number} height The new height
29280          * @param {Roo.EventObject} e The mouseup event
29281          */
29282         "resize" : true
29283     });
29284
29285     if(this.width !== null && this.height !== null){
29286         this.resizeTo(this.width, this.height);
29287     }else{
29288         this.updateChildSize();
29289     }
29290     if(Roo.isIE){
29291         this.el.dom.style.zoom = 1;
29292     }
29293     Roo.Resizable.superclass.constructor.call(this);
29294 };
29295
29296 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29297         resizeChild : false,
29298         adjustments : [0, 0],
29299         minWidth : 5,
29300         minHeight : 5,
29301         maxWidth : 10000,
29302         maxHeight : 10000,
29303         enabled : true,
29304         animate : false,
29305         duration : .35,
29306         dynamic : false,
29307         handles : false,
29308         multiDirectional : false,
29309         disableTrackOver : false,
29310         easing : 'easeOutStrong',
29311         widthIncrement : 0,
29312         heightIncrement : 0,
29313         pinned : false,
29314         width : null,
29315         height : null,
29316         preserveRatio : false,
29317         transparent: false,
29318         minX: 0,
29319         minY: 0,
29320         draggable: false,
29321
29322         /**
29323          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29324          */
29325         constrainTo: undefined,
29326         /**
29327          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29328          */
29329         resizeRegion: undefined,
29330
29331
29332     /**
29333      * Perform a manual resize
29334      * @param {Number} width
29335      * @param {Number} height
29336      */
29337     resizeTo : function(width, height){
29338         this.el.setSize(width, height);
29339         this.updateChildSize();
29340         this.fireEvent("resize", this, width, height, null);
29341     },
29342
29343     // private
29344     startSizing : function(e, handle){
29345         this.fireEvent("beforeresize", this, e);
29346         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29347
29348             if(!this.overlay){
29349                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29350                 this.overlay.unselectable();
29351                 this.overlay.enableDisplayMode("block");
29352                 this.overlay.on("mousemove", this.onMouseMove, this);
29353                 this.overlay.on("mouseup", this.onMouseUp, this);
29354             }
29355             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29356
29357             this.resizing = true;
29358             this.startBox = this.el.getBox();
29359             this.startPoint = e.getXY();
29360             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29361                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29362
29363             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29364             this.overlay.show();
29365
29366             if(this.constrainTo) {
29367                 var ct = Roo.get(this.constrainTo);
29368                 this.resizeRegion = ct.getRegion().adjust(
29369                     ct.getFrameWidth('t'),
29370                     ct.getFrameWidth('l'),
29371                     -ct.getFrameWidth('b'),
29372                     -ct.getFrameWidth('r')
29373                 );
29374             }
29375
29376             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29377             this.proxy.show();
29378             this.proxy.setBox(this.startBox);
29379             if(!this.dynamic){
29380                 this.proxy.setStyle('visibility', 'visible');
29381             }
29382         }
29383     },
29384
29385     // private
29386     onMouseDown : function(handle, e){
29387         if(this.enabled){
29388             e.stopEvent();
29389             this.activeHandle = handle;
29390             this.startSizing(e, handle);
29391         }
29392     },
29393
29394     // private
29395     onMouseUp : function(e){
29396         var size = this.resizeElement();
29397         this.resizing = false;
29398         this.handleOut();
29399         this.overlay.hide();
29400         this.proxy.hide();
29401         this.fireEvent("resize", this, size.width, size.height, e);
29402     },
29403
29404     // private
29405     updateChildSize : function(){
29406         
29407         if(this.resizeChild){
29408             var el = this.el;
29409             var child = this.resizeChild;
29410             var adj = this.adjustments;
29411             if(el.dom.offsetWidth){
29412                 var b = el.getSize(true);
29413                 child.setSize(b.width+adj[0], b.height+adj[1]);
29414             }
29415             // Second call here for IE
29416             // The first call enables instant resizing and
29417             // the second call corrects scroll bars if they
29418             // exist
29419             if(Roo.isIE){
29420                 setTimeout(function(){
29421                     if(el.dom.offsetWidth){
29422                         var b = el.getSize(true);
29423                         child.setSize(b.width+adj[0], b.height+adj[1]);
29424                     }
29425                 }, 10);
29426             }
29427         }
29428     },
29429
29430     // private
29431     snap : function(value, inc, min){
29432         if(!inc || !value) return value;
29433         var newValue = value;
29434         var m = value % inc;
29435         if(m > 0){
29436             if(m > (inc/2)){
29437                 newValue = value + (inc-m);
29438             }else{
29439                 newValue = value - m;
29440             }
29441         }
29442         return Math.max(min, newValue);
29443     },
29444
29445     // private
29446     resizeElement : function(){
29447         var box = this.proxy.getBox();
29448         if(this.updateBox){
29449             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29450         }else{
29451             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29452         }
29453         this.updateChildSize();
29454         if(!this.dynamic){
29455             this.proxy.hide();
29456         }
29457         return box;
29458     },
29459
29460     // private
29461     constrain : function(v, diff, m, mx){
29462         if(v - diff < m){
29463             diff = v - m;
29464         }else if(v - diff > mx){
29465             diff = mx - v;
29466         }
29467         return diff;
29468     },
29469
29470     // private
29471     onMouseMove : function(e){
29472         
29473         if(this.enabled){
29474             try{// try catch so if something goes wrong the user doesn't get hung
29475
29476             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29477                 return;
29478             }
29479
29480             //var curXY = this.startPoint;
29481             var curSize = this.curSize || this.startBox;
29482             var x = this.startBox.x, y = this.startBox.y;
29483             var ox = x, oy = y;
29484             var w = curSize.width, h = curSize.height;
29485             var ow = w, oh = h;
29486             var mw = this.minWidth, mh = this.minHeight;
29487             var mxw = this.maxWidth, mxh = this.maxHeight;
29488             var wi = this.widthIncrement;
29489             var hi = this.heightIncrement;
29490
29491             var eventXY = e.getXY();
29492             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29493             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29494
29495             var pos = this.activeHandle.position;
29496
29497             switch(pos){
29498                 case "east":
29499                     w += diffX;
29500                     w = Math.min(Math.max(mw, w), mxw);
29501                     break;
29502              
29503                 case "south":
29504                     h += diffY;
29505                     h = Math.min(Math.max(mh, h), mxh);
29506                     break;
29507                 case "southeast":
29508                     w += diffX;
29509                     h += diffY;
29510                     w = Math.min(Math.max(mw, w), mxw);
29511                     h = Math.min(Math.max(mh, h), mxh);
29512                     break;
29513                 case "north":
29514                     diffY = this.constrain(h, diffY, mh, mxh);
29515                     y += diffY;
29516                     h -= diffY;
29517                     break;
29518                 case "hdrag":
29519                     
29520                     if (wi) {
29521                         var adiffX = Math.abs(diffX);
29522                         var sub = (adiffX % wi); // how much 
29523                         if (sub > (wi/2)) { // far enough to snap
29524                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29525                         } else {
29526                             // remove difference.. 
29527                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29528                         }
29529                     }
29530                     x += diffX;
29531                     x = Math.max(this.minX, x);
29532                     break;
29533                 case "west":
29534                     diffX = this.constrain(w, diffX, mw, mxw);
29535                     x += diffX;
29536                     w -= diffX;
29537                     break;
29538                 case "northeast":
29539                     w += diffX;
29540                     w = Math.min(Math.max(mw, w), mxw);
29541                     diffY = this.constrain(h, diffY, mh, mxh);
29542                     y += diffY;
29543                     h -= diffY;
29544                     break;
29545                 case "northwest":
29546                     diffX = this.constrain(w, diffX, mw, mxw);
29547                     diffY = this.constrain(h, diffY, mh, mxh);
29548                     y += diffY;
29549                     h -= diffY;
29550                     x += diffX;
29551                     w -= diffX;
29552                     break;
29553                case "southwest":
29554                     diffX = this.constrain(w, diffX, mw, mxw);
29555                     h += diffY;
29556                     h = Math.min(Math.max(mh, h), mxh);
29557                     x += diffX;
29558                     w -= diffX;
29559                     break;
29560             }
29561
29562             var sw = this.snap(w, wi, mw);
29563             var sh = this.snap(h, hi, mh);
29564             if(sw != w || sh != h){
29565                 switch(pos){
29566                     case "northeast":
29567                         y -= sh - h;
29568                     break;
29569                     case "north":
29570                         y -= sh - h;
29571                         break;
29572                     case "southwest":
29573                         x -= sw - w;
29574                     break;
29575                     case "west":
29576                         x -= sw - w;
29577                         break;
29578                     case "northwest":
29579                         x -= sw - w;
29580                         y -= sh - h;
29581                     break;
29582                 }
29583                 w = sw;
29584                 h = sh;
29585             }
29586
29587             if(this.preserveRatio){
29588                 switch(pos){
29589                     case "southeast":
29590                     case "east":
29591                         h = oh * (w/ow);
29592                         h = Math.min(Math.max(mh, h), mxh);
29593                         w = ow * (h/oh);
29594                        break;
29595                     case "south":
29596                         w = ow * (h/oh);
29597                         w = Math.min(Math.max(mw, w), mxw);
29598                         h = oh * (w/ow);
29599                         break;
29600                     case "northeast":
29601                         w = ow * (h/oh);
29602                         w = Math.min(Math.max(mw, w), mxw);
29603                         h = oh * (w/ow);
29604                     break;
29605                     case "north":
29606                         var tw = w;
29607                         w = ow * (h/oh);
29608                         w = Math.min(Math.max(mw, w), mxw);
29609                         h = oh * (w/ow);
29610                         x += (tw - w) / 2;
29611                         break;
29612                     case "southwest":
29613                         h = oh * (w/ow);
29614                         h = Math.min(Math.max(mh, h), mxh);
29615                         var tw = w;
29616                         w = ow * (h/oh);
29617                         x += tw - w;
29618                         break;
29619                     case "west":
29620                         var th = h;
29621                         h = oh * (w/ow);
29622                         h = Math.min(Math.max(mh, h), mxh);
29623                         y += (th - h) / 2;
29624                         var tw = w;
29625                         w = ow * (h/oh);
29626                         x += tw - w;
29627                        break;
29628                     case "northwest":
29629                         var tw = w;
29630                         var th = h;
29631                         h = oh * (w/ow);
29632                         h = Math.min(Math.max(mh, h), mxh);
29633                         w = ow * (h/oh);
29634                         y += th - h;
29635                         x += tw - w;
29636                        break;
29637
29638                 }
29639             }
29640             if (pos == 'hdrag') {
29641                 w = ow;
29642             }
29643             this.proxy.setBounds(x, y, w, h);
29644             if(this.dynamic){
29645                 this.resizeElement();
29646             }
29647             }catch(e){}
29648         }
29649         this.fireEvent("resizing", this, x, y, w, h, e);
29650     },
29651
29652     // private
29653     handleOver : function(){
29654         if(this.enabled){
29655             this.el.addClass("x-resizable-over");
29656         }
29657     },
29658
29659     // private
29660     handleOut : function(){
29661         if(!this.resizing){
29662             this.el.removeClass("x-resizable-over");
29663         }
29664     },
29665
29666     /**
29667      * Returns the element this component is bound to.
29668      * @return {Roo.Element}
29669      */
29670     getEl : function(){
29671         return this.el;
29672     },
29673
29674     /**
29675      * Returns the resizeChild element (or null).
29676      * @return {Roo.Element}
29677      */
29678     getResizeChild : function(){
29679         return this.resizeChild;
29680     },
29681     groupHandler : function()
29682     {
29683         
29684     },
29685     /**
29686      * Destroys this resizable. If the element was wrapped and
29687      * removeEl is not true then the element remains.
29688      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29689      */
29690     destroy : function(removeEl){
29691         this.proxy.remove();
29692         if(this.overlay){
29693             this.overlay.removeAllListeners();
29694             this.overlay.remove();
29695         }
29696         var ps = Roo.Resizable.positions;
29697         for(var k in ps){
29698             if(typeof ps[k] != "function" && this[ps[k]]){
29699                 var h = this[ps[k]];
29700                 h.el.removeAllListeners();
29701                 h.el.remove();
29702             }
29703         }
29704         if(removeEl){
29705             this.el.update("");
29706             this.el.remove();
29707         }
29708     }
29709 });
29710
29711 // private
29712 // hash to map config positions to true positions
29713 Roo.Resizable.positions = {
29714     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29715     hd: "hdrag"
29716 };
29717
29718 // private
29719 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29720     if(!this.tpl){
29721         // only initialize the template if resizable is used
29722         var tpl = Roo.DomHelper.createTemplate(
29723             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29724         );
29725         tpl.compile();
29726         Roo.Resizable.Handle.prototype.tpl = tpl;
29727     }
29728     this.position = pos;
29729     this.rz = rz;
29730     // show north drag fro topdra
29731     var handlepos = pos == 'hdrag' ? 'north' : pos;
29732     
29733     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29734     if (pos == 'hdrag') {
29735         this.el.setStyle('cursor', 'pointer');
29736     }
29737     this.el.unselectable();
29738     if(transparent){
29739         this.el.setOpacity(0);
29740     }
29741     this.el.on("mousedown", this.onMouseDown, this);
29742     if(!disableTrackOver){
29743         this.el.on("mouseover", this.onMouseOver, this);
29744         this.el.on("mouseout", this.onMouseOut, this);
29745     }
29746 };
29747
29748 // private
29749 Roo.Resizable.Handle.prototype = {
29750     afterResize : function(rz){
29751         Roo.log('after?');
29752         // do nothing
29753     },
29754     // private
29755     onMouseDown : function(e){
29756         this.rz.onMouseDown(this, e);
29757     },
29758     // private
29759     onMouseOver : function(e){
29760         this.rz.handleOver(this, e);
29761     },
29762     // private
29763     onMouseOut : function(e){
29764         this.rz.handleOut(this, e);
29765     }
29766 };/*
29767  * Based on:
29768  * Ext JS Library 1.1.1
29769  * Copyright(c) 2006-2007, Ext JS, LLC.
29770  *
29771  * Originally Released Under LGPL - original licence link has changed is not relivant.
29772  *
29773  * Fork - LGPL
29774  * <script type="text/javascript">
29775  */
29776
29777 /**
29778  * @class Roo.Editor
29779  * @extends Roo.Component
29780  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29781  * @constructor
29782  * Create a new Editor
29783  * @param {Roo.form.Field} field The Field object (or descendant)
29784  * @param {Object} config The config object
29785  */
29786 Roo.Editor = function(field, config){
29787     Roo.Editor.superclass.constructor.call(this, config);
29788     this.field = field;
29789     this.addEvents({
29790         /**
29791              * @event beforestartedit
29792              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29793              * false from the handler of this event.
29794              * @param {Editor} this
29795              * @param {Roo.Element} boundEl The underlying element bound to this editor
29796              * @param {Mixed} value The field value being set
29797              */
29798         "beforestartedit" : true,
29799         /**
29800              * @event startedit
29801              * Fires when this editor is displayed
29802              * @param {Roo.Element} boundEl The underlying element bound to this editor
29803              * @param {Mixed} value The starting field value
29804              */
29805         "startedit" : true,
29806         /**
29807              * @event beforecomplete
29808              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29809              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29810              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29811              * event will not fire since no edit actually occurred.
29812              * @param {Editor} this
29813              * @param {Mixed} value The current field value
29814              * @param {Mixed} startValue The original field value
29815              */
29816         "beforecomplete" : true,
29817         /**
29818              * @event complete
29819              * Fires after editing is complete and any changed value has been written to the underlying field.
29820              * @param {Editor} this
29821              * @param {Mixed} value The current field value
29822              * @param {Mixed} startValue The original field value
29823              */
29824         "complete" : true,
29825         /**
29826          * @event specialkey
29827          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29828          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29829          * @param {Roo.form.Field} this
29830          * @param {Roo.EventObject} e The event object
29831          */
29832         "specialkey" : true
29833     });
29834 };
29835
29836 Roo.extend(Roo.Editor, Roo.Component, {
29837     /**
29838      * @cfg {Boolean/String} autosize
29839      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29840      * or "height" to adopt the height only (defaults to false)
29841      */
29842     /**
29843      * @cfg {Boolean} revertInvalid
29844      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29845      * validation fails (defaults to true)
29846      */
29847     /**
29848      * @cfg {Boolean} ignoreNoChange
29849      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29850      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29851      * will never be ignored.
29852      */
29853     /**
29854      * @cfg {Boolean} hideEl
29855      * False to keep the bound element visible while the editor is displayed (defaults to true)
29856      */
29857     /**
29858      * @cfg {Mixed} value
29859      * The data value of the underlying field (defaults to "")
29860      */
29861     value : "",
29862     /**
29863      * @cfg {String} alignment
29864      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29865      */
29866     alignment: "c-c?",
29867     /**
29868      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29869      * for bottom-right shadow (defaults to "frame")
29870      */
29871     shadow : "frame",
29872     /**
29873      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29874      */
29875     constrain : false,
29876     /**
29877      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29878      */
29879     completeOnEnter : false,
29880     /**
29881      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29882      */
29883     cancelOnEsc : false,
29884     /**
29885      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29886      */
29887     updateEl : false,
29888
29889     // private
29890     onRender : function(ct, position){
29891         this.el = new Roo.Layer({
29892             shadow: this.shadow,
29893             cls: "x-editor",
29894             parentEl : ct,
29895             shim : this.shim,
29896             shadowOffset:4,
29897             id: this.id,
29898             constrain: this.constrain
29899         });
29900         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29901         if(this.field.msgTarget != 'title'){
29902             this.field.msgTarget = 'qtip';
29903         }
29904         this.field.render(this.el);
29905         if(Roo.isGecko){
29906             this.field.el.dom.setAttribute('autocomplete', 'off');
29907         }
29908         this.field.on("specialkey", this.onSpecialKey, this);
29909         if(this.swallowKeys){
29910             this.field.el.swallowEvent(['keydown','keypress']);
29911         }
29912         this.field.show();
29913         this.field.on("blur", this.onBlur, this);
29914         if(this.field.grow){
29915             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29916         }
29917     },
29918
29919     onSpecialKey : function(field, e)
29920     {
29921         //Roo.log('editor onSpecialKey');
29922         if(this.completeOnEnter && e.getKey() == e.ENTER){
29923             e.stopEvent();
29924             this.completeEdit();
29925             return;
29926         }
29927         // do not fire special key otherwise it might hide close the editor...
29928         if(e.getKey() == e.ENTER){    
29929             return;
29930         }
29931         if(this.cancelOnEsc && e.getKey() == e.ESC){
29932             this.cancelEdit();
29933             return;
29934         } 
29935         this.fireEvent('specialkey', field, e);
29936     
29937     },
29938
29939     /**
29940      * Starts the editing process and shows the editor.
29941      * @param {String/HTMLElement/Element} el The element to edit
29942      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29943       * to the innerHTML of el.
29944      */
29945     startEdit : function(el, value){
29946         if(this.editing){
29947             this.completeEdit();
29948         }
29949         this.boundEl = Roo.get(el);
29950         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29951         if(!this.rendered){
29952             this.render(this.parentEl || document.body);
29953         }
29954         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29955             return;
29956         }
29957         this.startValue = v;
29958         this.field.setValue(v);
29959         if(this.autoSize){
29960             var sz = this.boundEl.getSize();
29961             switch(this.autoSize){
29962                 case "width":
29963                 this.setSize(sz.width,  "");
29964                 break;
29965                 case "height":
29966                 this.setSize("",  sz.height);
29967                 break;
29968                 default:
29969                 this.setSize(sz.width,  sz.height);
29970             }
29971         }
29972         this.el.alignTo(this.boundEl, this.alignment);
29973         this.editing = true;
29974         if(Roo.QuickTips){
29975             Roo.QuickTips.disable();
29976         }
29977         this.show();
29978     },
29979
29980     /**
29981      * Sets the height and width of this editor.
29982      * @param {Number} width The new width
29983      * @param {Number} height The new height
29984      */
29985     setSize : function(w, h){
29986         this.field.setSize(w, h);
29987         if(this.el){
29988             this.el.sync();
29989         }
29990     },
29991
29992     /**
29993      * Realigns the editor to the bound field based on the current alignment config value.
29994      */
29995     realign : function(){
29996         this.el.alignTo(this.boundEl, this.alignment);
29997     },
29998
29999     /**
30000      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30001      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30002      */
30003     completeEdit : function(remainVisible){
30004         if(!this.editing){
30005             return;
30006         }
30007         var v = this.getValue();
30008         if(this.revertInvalid !== false && !this.field.isValid()){
30009             v = this.startValue;
30010             this.cancelEdit(true);
30011         }
30012         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30013             this.editing = false;
30014             this.hide();
30015             return;
30016         }
30017         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30018             this.editing = false;
30019             if(this.updateEl && this.boundEl){
30020                 this.boundEl.update(v);
30021             }
30022             if(remainVisible !== true){
30023                 this.hide();
30024             }
30025             this.fireEvent("complete", this, v, this.startValue);
30026         }
30027     },
30028
30029     // private
30030     onShow : function(){
30031         this.el.show();
30032         if(this.hideEl !== false){
30033             this.boundEl.hide();
30034         }
30035         this.field.show();
30036         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30037             this.fixIEFocus = true;
30038             this.deferredFocus.defer(50, this);
30039         }else{
30040             this.field.focus();
30041         }
30042         this.fireEvent("startedit", this.boundEl, this.startValue);
30043     },
30044
30045     deferredFocus : function(){
30046         if(this.editing){
30047             this.field.focus();
30048         }
30049     },
30050
30051     /**
30052      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30053      * reverted to the original starting value.
30054      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30055      * cancel (defaults to false)
30056      */
30057     cancelEdit : function(remainVisible){
30058         if(this.editing){
30059             this.setValue(this.startValue);
30060             if(remainVisible !== true){
30061                 this.hide();
30062             }
30063         }
30064     },
30065
30066     // private
30067     onBlur : function(){
30068         if(this.allowBlur !== true && this.editing){
30069             this.completeEdit();
30070         }
30071     },
30072
30073     // private
30074     onHide : function(){
30075         if(this.editing){
30076             this.completeEdit();
30077             return;
30078         }
30079         this.field.blur();
30080         if(this.field.collapse){
30081             this.field.collapse();
30082         }
30083         this.el.hide();
30084         if(this.hideEl !== false){
30085             this.boundEl.show();
30086         }
30087         if(Roo.QuickTips){
30088             Roo.QuickTips.enable();
30089         }
30090     },
30091
30092     /**
30093      * Sets the data value of the editor
30094      * @param {Mixed} value Any valid value supported by the underlying field
30095      */
30096     setValue : function(v){
30097         this.field.setValue(v);
30098     },
30099
30100     /**
30101      * Gets the data value of the editor
30102      * @return {Mixed} The data value
30103      */
30104     getValue : function(){
30105         return this.field.getValue();
30106     }
30107 });/*
30108  * Based on:
30109  * Ext JS Library 1.1.1
30110  * Copyright(c) 2006-2007, Ext JS, LLC.
30111  *
30112  * Originally Released Under LGPL - original licence link has changed is not relivant.
30113  *
30114  * Fork - LGPL
30115  * <script type="text/javascript">
30116  */
30117  
30118 /**
30119  * @class Roo.BasicDialog
30120  * @extends Roo.util.Observable
30121  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30122  * <pre><code>
30123 var dlg = new Roo.BasicDialog("my-dlg", {
30124     height: 200,
30125     width: 300,
30126     minHeight: 100,
30127     minWidth: 150,
30128     modal: true,
30129     proxyDrag: true,
30130     shadow: true
30131 });
30132 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30133 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30134 dlg.addButton('Cancel', dlg.hide, dlg);
30135 dlg.show();
30136 </code></pre>
30137   <b>A Dialog should always be a direct child of the body element.</b>
30138  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30139  * @cfg {String} title Default text to display in the title bar (defaults to null)
30140  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30141  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30142  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30143  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30144  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30145  * (defaults to null with no animation)
30146  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30147  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30148  * property for valid values (defaults to 'all')
30149  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30150  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30151  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30152  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30153  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30154  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30155  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30156  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30157  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30158  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30159  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30160  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30161  * draggable = true (defaults to false)
30162  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30163  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30164  * shadow (defaults to false)
30165  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30166  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30167  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30168  * @cfg {Array} buttons Array of buttons
30169  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30170  * @constructor
30171  * Create a new BasicDialog.
30172  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30173  * @param {Object} config Configuration options
30174  */
30175 Roo.BasicDialog = function(el, config){
30176     this.el = Roo.get(el);
30177     var dh = Roo.DomHelper;
30178     if(!this.el && config && config.autoCreate){
30179         if(typeof config.autoCreate == "object"){
30180             if(!config.autoCreate.id){
30181                 config.autoCreate.id = el;
30182             }
30183             this.el = dh.append(document.body,
30184                         config.autoCreate, true);
30185         }else{
30186             this.el = dh.append(document.body,
30187                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30188         }
30189     }
30190     el = this.el;
30191     el.setDisplayed(true);
30192     el.hide = this.hideAction;
30193     this.id = el.id;
30194     el.addClass("x-dlg");
30195
30196     Roo.apply(this, config);
30197
30198     this.proxy = el.createProxy("x-dlg-proxy");
30199     this.proxy.hide = this.hideAction;
30200     this.proxy.setOpacity(.5);
30201     this.proxy.hide();
30202
30203     if(config.width){
30204         el.setWidth(config.width);
30205     }
30206     if(config.height){
30207         el.setHeight(config.height);
30208     }
30209     this.size = el.getSize();
30210     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30211         this.xy = [config.x,config.y];
30212     }else{
30213         this.xy = el.getCenterXY(true);
30214     }
30215     /** The header element @type Roo.Element */
30216     this.header = el.child("> .x-dlg-hd");
30217     /** The body element @type Roo.Element */
30218     this.body = el.child("> .x-dlg-bd");
30219     /** The footer element @type Roo.Element */
30220     this.footer = el.child("> .x-dlg-ft");
30221
30222     if(!this.header){
30223         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30224     }
30225     if(!this.body){
30226         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30227     }
30228
30229     this.header.unselectable();
30230     if(this.title){
30231         this.header.update(this.title);
30232     }
30233     // this element allows the dialog to be focused for keyboard event
30234     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30235     this.focusEl.swallowEvent("click", true);
30236
30237     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30238
30239     // wrap the body and footer for special rendering
30240     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30241     if(this.footer){
30242         this.bwrap.dom.appendChild(this.footer.dom);
30243     }
30244
30245     this.bg = this.el.createChild({
30246         tag: "div", cls:"x-dlg-bg",
30247         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30248     });
30249     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30250
30251
30252     if(this.autoScroll !== false && !this.autoTabs){
30253         this.body.setStyle("overflow", "auto");
30254     }
30255
30256     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30257
30258     if(this.closable !== false){
30259         this.el.addClass("x-dlg-closable");
30260         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30261         this.close.on("click", this.closeClick, this);
30262         this.close.addClassOnOver("x-dlg-close-over");
30263     }
30264     if(this.collapsible !== false){
30265         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30266         this.collapseBtn.on("click", this.collapseClick, this);
30267         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30268         this.header.on("dblclick", this.collapseClick, this);
30269     }
30270     if(this.resizable !== false){
30271         this.el.addClass("x-dlg-resizable");
30272         this.resizer = new Roo.Resizable(el, {
30273             minWidth: this.minWidth || 80,
30274             minHeight:this.minHeight || 80,
30275             handles: this.resizeHandles || "all",
30276             pinned: true
30277         });
30278         this.resizer.on("beforeresize", this.beforeResize, this);
30279         this.resizer.on("resize", this.onResize, this);
30280     }
30281     if(this.draggable !== false){
30282         el.addClass("x-dlg-draggable");
30283         if (!this.proxyDrag) {
30284             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30285         }
30286         else {
30287             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30288         }
30289         dd.setHandleElId(this.header.id);
30290         dd.endDrag = this.endMove.createDelegate(this);
30291         dd.startDrag = this.startMove.createDelegate(this);
30292         dd.onDrag = this.onDrag.createDelegate(this);
30293         dd.scroll = false;
30294         this.dd = dd;
30295     }
30296     if(this.modal){
30297         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30298         this.mask.enableDisplayMode("block");
30299         this.mask.hide();
30300         this.el.addClass("x-dlg-modal");
30301     }
30302     if(this.shadow){
30303         this.shadow = new Roo.Shadow({
30304             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30305             offset : this.shadowOffset
30306         });
30307     }else{
30308         this.shadowOffset = 0;
30309     }
30310     if(Roo.useShims && this.shim !== false){
30311         this.shim = this.el.createShim();
30312         this.shim.hide = this.hideAction;
30313         this.shim.hide();
30314     }else{
30315         this.shim = false;
30316     }
30317     if(this.autoTabs){
30318         this.initTabs();
30319     }
30320     if (this.buttons) { 
30321         var bts= this.buttons;
30322         this.buttons = [];
30323         Roo.each(bts, function(b) {
30324             this.addButton(b);
30325         }, this);
30326     }
30327     
30328     
30329     this.addEvents({
30330         /**
30331          * @event keydown
30332          * Fires when a key is pressed
30333          * @param {Roo.BasicDialog} this
30334          * @param {Roo.EventObject} e
30335          */
30336         "keydown" : true,
30337         /**
30338          * @event move
30339          * Fires when this dialog is moved by the user.
30340          * @param {Roo.BasicDialog} this
30341          * @param {Number} x The new page X
30342          * @param {Number} y The new page Y
30343          */
30344         "move" : true,
30345         /**
30346          * @event resize
30347          * Fires when this dialog is resized by the user.
30348          * @param {Roo.BasicDialog} this
30349          * @param {Number} width The new width
30350          * @param {Number} height The new height
30351          */
30352         "resize" : true,
30353         /**
30354          * @event beforehide
30355          * Fires before this dialog is hidden.
30356          * @param {Roo.BasicDialog} this
30357          */
30358         "beforehide" : true,
30359         /**
30360          * @event hide
30361          * Fires when this dialog is hidden.
30362          * @param {Roo.BasicDialog} this
30363          */
30364         "hide" : true,
30365         /**
30366          * @event beforeshow
30367          * Fires before this dialog is shown.
30368          * @param {Roo.BasicDialog} this
30369          */
30370         "beforeshow" : true,
30371         /**
30372          * @event show
30373          * Fires when this dialog is shown.
30374          * @param {Roo.BasicDialog} this
30375          */
30376         "show" : true
30377     });
30378     el.on("keydown", this.onKeyDown, this);
30379     el.on("mousedown", this.toFront, this);
30380     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30381     this.el.hide();
30382     Roo.DialogManager.register(this);
30383     Roo.BasicDialog.superclass.constructor.call(this);
30384 };
30385
30386 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30387     shadowOffset: Roo.isIE ? 6 : 5,
30388     minHeight: 80,
30389     minWidth: 200,
30390     minButtonWidth: 75,
30391     defaultButton: null,
30392     buttonAlign: "right",
30393     tabTag: 'div',
30394     firstShow: true,
30395
30396     /**
30397      * Sets the dialog title text
30398      * @param {String} text The title text to display
30399      * @return {Roo.BasicDialog} this
30400      */
30401     setTitle : function(text){
30402         this.header.update(text);
30403         return this;
30404     },
30405
30406     // private
30407     closeClick : function(){
30408         this.hide();
30409     },
30410
30411     // private
30412     collapseClick : function(){
30413         this[this.collapsed ? "expand" : "collapse"]();
30414     },
30415
30416     /**
30417      * Collapses the dialog to its minimized state (only the title bar is visible).
30418      * Equivalent to the user clicking the collapse dialog button.
30419      */
30420     collapse : function(){
30421         if(!this.collapsed){
30422             this.collapsed = true;
30423             this.el.addClass("x-dlg-collapsed");
30424             this.restoreHeight = this.el.getHeight();
30425             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30426         }
30427     },
30428
30429     /**
30430      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30431      * clicking the expand dialog button.
30432      */
30433     expand : function(){
30434         if(this.collapsed){
30435             this.collapsed = false;
30436             this.el.removeClass("x-dlg-collapsed");
30437             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30438         }
30439     },
30440
30441     /**
30442      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30443      * @return {Roo.TabPanel} The tabs component
30444      */
30445     initTabs : function(){
30446         var tabs = this.getTabs();
30447         while(tabs.getTab(0)){
30448             tabs.removeTab(0);
30449         }
30450         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30451             var dom = el.dom;
30452             tabs.addTab(Roo.id(dom), dom.title);
30453             dom.title = "";
30454         });
30455         tabs.activate(0);
30456         return tabs;
30457     },
30458
30459     // private
30460     beforeResize : function(){
30461         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30462     },
30463
30464     // private
30465     onResize : function(){
30466         this.refreshSize();
30467         this.syncBodyHeight();
30468         this.adjustAssets();
30469         this.focus();
30470         this.fireEvent("resize", this, this.size.width, this.size.height);
30471     },
30472
30473     // private
30474     onKeyDown : function(e){
30475         if(this.isVisible()){
30476             this.fireEvent("keydown", this, e);
30477         }
30478     },
30479
30480     /**
30481      * Resizes the dialog.
30482      * @param {Number} width
30483      * @param {Number} height
30484      * @return {Roo.BasicDialog} this
30485      */
30486     resizeTo : function(width, height){
30487         this.el.setSize(width, height);
30488         this.size = {width: width, height: height};
30489         this.syncBodyHeight();
30490         if(this.fixedcenter){
30491             this.center();
30492         }
30493         if(this.isVisible()){
30494             this.constrainXY();
30495             this.adjustAssets();
30496         }
30497         this.fireEvent("resize", this, width, height);
30498         return this;
30499     },
30500
30501
30502     /**
30503      * Resizes the dialog to fit the specified content size.
30504      * @param {Number} width
30505      * @param {Number} height
30506      * @return {Roo.BasicDialog} this
30507      */
30508     setContentSize : function(w, h){
30509         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30510         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30511         //if(!this.el.isBorderBox()){
30512             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30513             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30514         //}
30515         if(this.tabs){
30516             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30517             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30518         }
30519         this.resizeTo(w, h);
30520         return this;
30521     },
30522
30523     /**
30524      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30525      * executed in response to a particular key being pressed while the dialog is active.
30526      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30527      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30528      * @param {Function} fn The function to call
30529      * @param {Object} scope (optional) The scope of the function
30530      * @return {Roo.BasicDialog} this
30531      */
30532     addKeyListener : function(key, fn, scope){
30533         var keyCode, shift, ctrl, alt;
30534         if(typeof key == "object" && !(key instanceof Array)){
30535             keyCode = key["key"];
30536             shift = key["shift"];
30537             ctrl = key["ctrl"];
30538             alt = key["alt"];
30539         }else{
30540             keyCode = key;
30541         }
30542         var handler = function(dlg, e){
30543             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30544                 var k = e.getKey();
30545                 if(keyCode instanceof Array){
30546                     for(var i = 0, len = keyCode.length; i < len; i++){
30547                         if(keyCode[i] == k){
30548                           fn.call(scope || window, dlg, k, e);
30549                           return;
30550                         }
30551                     }
30552                 }else{
30553                     if(k == keyCode){
30554                         fn.call(scope || window, dlg, k, e);
30555                     }
30556                 }
30557             }
30558         };
30559         this.on("keydown", handler);
30560         return this;
30561     },
30562
30563     /**
30564      * Returns the TabPanel component (creates it if it doesn't exist).
30565      * Note: If you wish to simply check for the existence of tabs without creating them,
30566      * check for a null 'tabs' property.
30567      * @return {Roo.TabPanel} The tabs component
30568      */
30569     getTabs : function(){
30570         if(!this.tabs){
30571             this.el.addClass("x-dlg-auto-tabs");
30572             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30573             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30574         }
30575         return this.tabs;
30576     },
30577
30578     /**
30579      * Adds a button to the footer section of the dialog.
30580      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30581      * object or a valid Roo.DomHelper element config
30582      * @param {Function} handler The function called when the button is clicked
30583      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30584      * @return {Roo.Button} The new button
30585      */
30586     addButton : function(config, handler, scope){
30587         var dh = Roo.DomHelper;
30588         if(!this.footer){
30589             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30590         }
30591         if(!this.btnContainer){
30592             var tb = this.footer.createChild({
30593
30594                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30595                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30596             }, null, true);
30597             this.btnContainer = tb.firstChild.firstChild.firstChild;
30598         }
30599         var bconfig = {
30600             handler: handler,
30601             scope: scope,
30602             minWidth: this.minButtonWidth,
30603             hideParent:true
30604         };
30605         if(typeof config == "string"){
30606             bconfig.text = config;
30607         }else{
30608             if(config.tag){
30609                 bconfig.dhconfig = config;
30610             }else{
30611                 Roo.apply(bconfig, config);
30612             }
30613         }
30614         var fc = false;
30615         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30616             bconfig.position = Math.max(0, bconfig.position);
30617             fc = this.btnContainer.childNodes[bconfig.position];
30618         }
30619          
30620         var btn = new Roo.Button(
30621             fc ? 
30622                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30623                 : this.btnContainer.appendChild(document.createElement("td")),
30624             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30625             bconfig
30626         );
30627         this.syncBodyHeight();
30628         if(!this.buttons){
30629             /**
30630              * Array of all the buttons that have been added to this dialog via addButton
30631              * @type Array
30632              */
30633             this.buttons = [];
30634         }
30635         this.buttons.push(btn);
30636         return btn;
30637     },
30638
30639     /**
30640      * Sets the default button to be focused when the dialog is displayed.
30641      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30642      * @return {Roo.BasicDialog} this
30643      */
30644     setDefaultButton : function(btn){
30645         this.defaultButton = btn;
30646         return this;
30647     },
30648
30649     // private
30650     getHeaderFooterHeight : function(safe){
30651         var height = 0;
30652         if(this.header){
30653            height += this.header.getHeight();
30654         }
30655         if(this.footer){
30656            var fm = this.footer.getMargins();
30657             height += (this.footer.getHeight()+fm.top+fm.bottom);
30658         }
30659         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30660         height += this.centerBg.getPadding("tb");
30661         return height;
30662     },
30663
30664     // private
30665     syncBodyHeight : function()
30666     {
30667         var bd = this.body, // the text
30668             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30669             bw = this.bwrap;
30670         var height = this.size.height - this.getHeaderFooterHeight(false);
30671         bd.setHeight(height-bd.getMargins("tb"));
30672         var hh = this.header.getHeight();
30673         var h = this.size.height-hh;
30674         cb.setHeight(h);
30675         
30676         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30677         bw.setHeight(h-cb.getPadding("tb"));
30678         
30679         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30680         bd.setWidth(bw.getWidth(true));
30681         if(this.tabs){
30682             this.tabs.syncHeight();
30683             if(Roo.isIE){
30684                 this.tabs.el.repaint();
30685             }
30686         }
30687     },
30688
30689     /**
30690      * Restores the previous state of the dialog if Roo.state is configured.
30691      * @return {Roo.BasicDialog} this
30692      */
30693     restoreState : function(){
30694         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30695         if(box && box.width){
30696             this.xy = [box.x, box.y];
30697             this.resizeTo(box.width, box.height);
30698         }
30699         return this;
30700     },
30701
30702     // private
30703     beforeShow : function(){
30704         this.expand();
30705         if(this.fixedcenter){
30706             this.xy = this.el.getCenterXY(true);
30707         }
30708         if(this.modal){
30709             Roo.get(document.body).addClass("x-body-masked");
30710             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30711             this.mask.show();
30712         }
30713         this.constrainXY();
30714     },
30715
30716     // private
30717     animShow : function(){
30718         var b = Roo.get(this.animateTarget).getBox();
30719         this.proxy.setSize(b.width, b.height);
30720         this.proxy.setLocation(b.x, b.y);
30721         this.proxy.show();
30722         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30723                     true, .35, this.showEl.createDelegate(this));
30724     },
30725
30726     /**
30727      * Shows the dialog.
30728      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30729      * @return {Roo.BasicDialog} this
30730      */
30731     show : function(animateTarget){
30732         if (this.fireEvent("beforeshow", this) === false){
30733             return;
30734         }
30735         if(this.syncHeightBeforeShow){
30736             this.syncBodyHeight();
30737         }else if(this.firstShow){
30738             this.firstShow = false;
30739             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30740         }
30741         this.animateTarget = animateTarget || this.animateTarget;
30742         if(!this.el.isVisible()){
30743             this.beforeShow();
30744             if(this.animateTarget && Roo.get(this.animateTarget)){
30745                 this.animShow();
30746             }else{
30747                 this.showEl();
30748             }
30749         }
30750         return this;
30751     },
30752
30753     // private
30754     showEl : function(){
30755         this.proxy.hide();
30756         this.el.setXY(this.xy);
30757         this.el.show();
30758         this.adjustAssets(true);
30759         this.toFront();
30760         this.focus();
30761         // IE peekaboo bug - fix found by Dave Fenwick
30762         if(Roo.isIE){
30763             this.el.repaint();
30764         }
30765         this.fireEvent("show", this);
30766     },
30767
30768     /**
30769      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30770      * dialog itself will receive focus.
30771      */
30772     focus : function(){
30773         if(this.defaultButton){
30774             this.defaultButton.focus();
30775         }else{
30776             this.focusEl.focus();
30777         }
30778     },
30779
30780     // private
30781     constrainXY : function(){
30782         if(this.constraintoviewport !== false){
30783             if(!this.viewSize){
30784                 if(this.container){
30785                     var s = this.container.getSize();
30786                     this.viewSize = [s.width, s.height];
30787                 }else{
30788                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30789                 }
30790             }
30791             var s = Roo.get(this.container||document).getScroll();
30792
30793             var x = this.xy[0], y = this.xy[1];
30794             var w = this.size.width, h = this.size.height;
30795             var vw = this.viewSize[0], vh = this.viewSize[1];
30796             // only move it if it needs it
30797             var moved = false;
30798             // first validate right/bottom
30799             if(x + w > vw+s.left){
30800                 x = vw - w;
30801                 moved = true;
30802             }
30803             if(y + h > vh+s.top){
30804                 y = vh - h;
30805                 moved = true;
30806             }
30807             // then make sure top/left isn't negative
30808             if(x < s.left){
30809                 x = s.left;
30810                 moved = true;
30811             }
30812             if(y < s.top){
30813                 y = s.top;
30814                 moved = true;
30815             }
30816             if(moved){
30817                 // cache xy
30818                 this.xy = [x, y];
30819                 if(this.isVisible()){
30820                     this.el.setLocation(x, y);
30821                     this.adjustAssets();
30822                 }
30823             }
30824         }
30825     },
30826
30827     // private
30828     onDrag : function(){
30829         if(!this.proxyDrag){
30830             this.xy = this.el.getXY();
30831             this.adjustAssets();
30832         }
30833     },
30834
30835     // private
30836     adjustAssets : function(doShow){
30837         var x = this.xy[0], y = this.xy[1];
30838         var w = this.size.width, h = this.size.height;
30839         if(doShow === true){
30840             if(this.shadow){
30841                 this.shadow.show(this.el);
30842             }
30843             if(this.shim){
30844                 this.shim.show();
30845             }
30846         }
30847         if(this.shadow && this.shadow.isVisible()){
30848             this.shadow.show(this.el);
30849         }
30850         if(this.shim && this.shim.isVisible()){
30851             this.shim.setBounds(x, y, w, h);
30852         }
30853     },
30854
30855     // private
30856     adjustViewport : function(w, h){
30857         if(!w || !h){
30858             w = Roo.lib.Dom.getViewWidth();
30859             h = Roo.lib.Dom.getViewHeight();
30860         }
30861         // cache the size
30862         this.viewSize = [w, h];
30863         if(this.modal && this.mask.isVisible()){
30864             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30865             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30866         }
30867         if(this.isVisible()){
30868             this.constrainXY();
30869         }
30870     },
30871
30872     /**
30873      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30874      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30875      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30876      */
30877     destroy : function(removeEl){
30878         if(this.isVisible()){
30879             this.animateTarget = null;
30880             this.hide();
30881         }
30882         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30883         if(this.tabs){
30884             this.tabs.destroy(removeEl);
30885         }
30886         Roo.destroy(
30887              this.shim,
30888              this.proxy,
30889              this.resizer,
30890              this.close,
30891              this.mask
30892         );
30893         if(this.dd){
30894             this.dd.unreg();
30895         }
30896         if(this.buttons){
30897            for(var i = 0, len = this.buttons.length; i < len; i++){
30898                this.buttons[i].destroy();
30899            }
30900         }
30901         this.el.removeAllListeners();
30902         if(removeEl === true){
30903             this.el.update("");
30904             this.el.remove();
30905         }
30906         Roo.DialogManager.unregister(this);
30907     },
30908
30909     // private
30910     startMove : function(){
30911         if(this.proxyDrag){
30912             this.proxy.show();
30913         }
30914         if(this.constraintoviewport !== false){
30915             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30916         }
30917     },
30918
30919     // private
30920     endMove : function(){
30921         if(!this.proxyDrag){
30922             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30923         }else{
30924             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30925             this.proxy.hide();
30926         }
30927         this.refreshSize();
30928         this.adjustAssets();
30929         this.focus();
30930         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30931     },
30932
30933     /**
30934      * Brings this dialog to the front of any other visible dialogs
30935      * @return {Roo.BasicDialog} this
30936      */
30937     toFront : function(){
30938         Roo.DialogManager.bringToFront(this);
30939         return this;
30940     },
30941
30942     /**
30943      * Sends this dialog to the back (under) of any other visible dialogs
30944      * @return {Roo.BasicDialog} this
30945      */
30946     toBack : function(){
30947         Roo.DialogManager.sendToBack(this);
30948         return this;
30949     },
30950
30951     /**
30952      * Centers this dialog in the viewport
30953      * @return {Roo.BasicDialog} this
30954      */
30955     center : function(){
30956         var xy = this.el.getCenterXY(true);
30957         this.moveTo(xy[0], xy[1]);
30958         return this;
30959     },
30960
30961     /**
30962      * Moves the dialog's top-left corner to the specified point
30963      * @param {Number} x
30964      * @param {Number} y
30965      * @return {Roo.BasicDialog} this
30966      */
30967     moveTo : function(x, y){
30968         this.xy = [x,y];
30969         if(this.isVisible()){
30970             this.el.setXY(this.xy);
30971             this.adjustAssets();
30972         }
30973         return this;
30974     },
30975
30976     /**
30977      * Aligns the dialog to the specified element
30978      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30979      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30980      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30981      * @return {Roo.BasicDialog} this
30982      */
30983     alignTo : function(element, position, offsets){
30984         this.xy = this.el.getAlignToXY(element, position, offsets);
30985         if(this.isVisible()){
30986             this.el.setXY(this.xy);
30987             this.adjustAssets();
30988         }
30989         return this;
30990     },
30991
30992     /**
30993      * Anchors an element to another element and realigns it when the window is resized.
30994      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30995      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30996      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30997      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30998      * is a number, it is used as the buffer delay (defaults to 50ms).
30999      * @return {Roo.BasicDialog} this
31000      */
31001     anchorTo : function(el, alignment, offsets, monitorScroll){
31002         var action = function(){
31003             this.alignTo(el, alignment, offsets);
31004         };
31005         Roo.EventManager.onWindowResize(action, this);
31006         var tm = typeof monitorScroll;
31007         if(tm != 'undefined'){
31008             Roo.EventManager.on(window, 'scroll', action, this,
31009                 {buffer: tm == 'number' ? monitorScroll : 50});
31010         }
31011         action.call(this);
31012         return this;
31013     },
31014
31015     /**
31016      * Returns true if the dialog is visible
31017      * @return {Boolean}
31018      */
31019     isVisible : function(){
31020         return this.el.isVisible();
31021     },
31022
31023     // private
31024     animHide : function(callback){
31025         var b = Roo.get(this.animateTarget).getBox();
31026         this.proxy.show();
31027         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31028         this.el.hide();
31029         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31030                     this.hideEl.createDelegate(this, [callback]));
31031     },
31032
31033     /**
31034      * Hides the dialog.
31035      * @param {Function} callback (optional) Function to call when the dialog is hidden
31036      * @return {Roo.BasicDialog} this
31037      */
31038     hide : function(callback){
31039         if (this.fireEvent("beforehide", this) === false){
31040             return;
31041         }
31042         if(this.shadow){
31043             this.shadow.hide();
31044         }
31045         if(this.shim) {
31046           this.shim.hide();
31047         }
31048         // sometimes animateTarget seems to get set.. causing problems...
31049         // this just double checks..
31050         if(this.animateTarget && Roo.get(this.animateTarget)) {
31051            this.animHide(callback);
31052         }else{
31053             this.el.hide();
31054             this.hideEl(callback);
31055         }
31056         return this;
31057     },
31058
31059     // private
31060     hideEl : function(callback){
31061         this.proxy.hide();
31062         if(this.modal){
31063             this.mask.hide();
31064             Roo.get(document.body).removeClass("x-body-masked");
31065         }
31066         this.fireEvent("hide", this);
31067         if(typeof callback == "function"){
31068             callback();
31069         }
31070     },
31071
31072     // private
31073     hideAction : function(){
31074         this.setLeft("-10000px");
31075         this.setTop("-10000px");
31076         this.setStyle("visibility", "hidden");
31077     },
31078
31079     // private
31080     refreshSize : function(){
31081         this.size = this.el.getSize();
31082         this.xy = this.el.getXY();
31083         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31084     },
31085
31086     // private
31087     // z-index is managed by the DialogManager and may be overwritten at any time
31088     setZIndex : function(index){
31089         if(this.modal){
31090             this.mask.setStyle("z-index", index);
31091         }
31092         if(this.shim){
31093             this.shim.setStyle("z-index", ++index);
31094         }
31095         if(this.shadow){
31096             this.shadow.setZIndex(++index);
31097         }
31098         this.el.setStyle("z-index", ++index);
31099         if(this.proxy){
31100             this.proxy.setStyle("z-index", ++index);
31101         }
31102         if(this.resizer){
31103             this.resizer.proxy.setStyle("z-index", ++index);
31104         }
31105
31106         this.lastZIndex = index;
31107     },
31108
31109     /**
31110      * Returns the element for this dialog
31111      * @return {Roo.Element} The underlying dialog Element
31112      */
31113     getEl : function(){
31114         return this.el;
31115     }
31116 });
31117
31118 /**
31119  * @class Roo.DialogManager
31120  * Provides global access to BasicDialogs that have been created and
31121  * support for z-indexing (layering) multiple open dialogs.
31122  */
31123 Roo.DialogManager = function(){
31124     var list = {};
31125     var accessList = [];
31126     var front = null;
31127
31128     // private
31129     var sortDialogs = function(d1, d2){
31130         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31131     };
31132
31133     // private
31134     var orderDialogs = function(){
31135         accessList.sort(sortDialogs);
31136         var seed = Roo.DialogManager.zseed;
31137         for(var i = 0, len = accessList.length; i < len; i++){
31138             var dlg = accessList[i];
31139             if(dlg){
31140                 dlg.setZIndex(seed + (i*10));
31141             }
31142         }
31143     };
31144
31145     return {
31146         /**
31147          * The starting z-index for BasicDialogs (defaults to 9000)
31148          * @type Number The z-index value
31149          */
31150         zseed : 9000,
31151
31152         // private
31153         register : function(dlg){
31154             list[dlg.id] = dlg;
31155             accessList.push(dlg);
31156         },
31157
31158         // private
31159         unregister : function(dlg){
31160             delete list[dlg.id];
31161             var i=0;
31162             var len=0;
31163             if(!accessList.indexOf){
31164                 for(  i = 0, len = accessList.length; i < len; i++){
31165                     if(accessList[i] == dlg){
31166                         accessList.splice(i, 1);
31167                         return;
31168                     }
31169                 }
31170             }else{
31171                  i = accessList.indexOf(dlg);
31172                 if(i != -1){
31173                     accessList.splice(i, 1);
31174                 }
31175             }
31176         },
31177
31178         /**
31179          * Gets a registered dialog by id
31180          * @param {String/Object} id The id of the dialog or a dialog
31181          * @return {Roo.BasicDialog} this
31182          */
31183         get : function(id){
31184             return typeof id == "object" ? id : list[id];
31185         },
31186
31187         /**
31188          * Brings the specified dialog to the front
31189          * @param {String/Object} dlg The id of the dialog or a dialog
31190          * @return {Roo.BasicDialog} this
31191          */
31192         bringToFront : function(dlg){
31193             dlg = this.get(dlg);
31194             if(dlg != front){
31195                 front = dlg;
31196                 dlg._lastAccess = new Date().getTime();
31197                 orderDialogs();
31198             }
31199             return dlg;
31200         },
31201
31202         /**
31203          * Sends the specified dialog to the back
31204          * @param {String/Object} dlg The id of the dialog or a dialog
31205          * @return {Roo.BasicDialog} this
31206          */
31207         sendToBack : function(dlg){
31208             dlg = this.get(dlg);
31209             dlg._lastAccess = -(new Date().getTime());
31210             orderDialogs();
31211             return dlg;
31212         },
31213
31214         /**
31215          * Hides all dialogs
31216          */
31217         hideAll : function(){
31218             for(var id in list){
31219                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31220                     list[id].hide();
31221                 }
31222             }
31223         }
31224     };
31225 }();
31226
31227 /**
31228  * @class Roo.LayoutDialog
31229  * @extends Roo.BasicDialog
31230  * Dialog which provides adjustments for working with a layout in a Dialog.
31231  * Add your necessary layout config options to the dialog's config.<br>
31232  * Example usage (including a nested layout):
31233  * <pre><code>
31234 if(!dialog){
31235     dialog = new Roo.LayoutDialog("download-dlg", {
31236         modal: true,
31237         width:600,
31238         height:450,
31239         shadow:true,
31240         minWidth:500,
31241         minHeight:350,
31242         autoTabs:true,
31243         proxyDrag:true,
31244         // layout config merges with the dialog config
31245         center:{
31246             tabPosition: "top",
31247             alwaysShowTabs: true
31248         }
31249     });
31250     dialog.addKeyListener(27, dialog.hide, dialog);
31251     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31252     dialog.addButton("Build It!", this.getDownload, this);
31253
31254     // we can even add nested layouts
31255     var innerLayout = new Roo.BorderLayout("dl-inner", {
31256         east: {
31257             initialSize: 200,
31258             autoScroll:true,
31259             split:true
31260         },
31261         center: {
31262             autoScroll:true
31263         }
31264     });
31265     innerLayout.beginUpdate();
31266     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31267     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31268     innerLayout.endUpdate(true);
31269
31270     var layout = dialog.getLayout();
31271     layout.beginUpdate();
31272     layout.add("center", new Roo.ContentPanel("standard-panel",
31273                         {title: "Download the Source", fitToFrame:true}));
31274     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31275                {title: "Build your own roo.js"}));
31276     layout.getRegion("center").showPanel(sp);
31277     layout.endUpdate();
31278 }
31279 </code></pre>
31280     * @constructor
31281     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31282     * @param {Object} config configuration options
31283   */
31284 Roo.LayoutDialog = function(el, cfg){
31285     
31286     var config=  cfg;
31287     if (typeof(cfg) == 'undefined') {
31288         config = Roo.apply({}, el);
31289         // not sure why we use documentElement here.. - it should always be body.
31290         // IE7 borks horribly if we use documentElement.
31291         // webkit also does not like documentElement - it creates a body element...
31292         el = Roo.get( document.body || document.documentElement ).createChild();
31293         //config.autoCreate = true;
31294     }
31295     
31296     
31297     config.autoTabs = false;
31298     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31299     this.body.setStyle({overflow:"hidden", position:"relative"});
31300     this.layout = new Roo.BorderLayout(this.body.dom, config);
31301     this.layout.monitorWindowResize = false;
31302     this.el.addClass("x-dlg-auto-layout");
31303     // fix case when center region overwrites center function
31304     this.center = Roo.BasicDialog.prototype.center;
31305     this.on("show", this.layout.layout, this.layout, true);
31306     if (config.items) {
31307         var xitems = config.items;
31308         delete config.items;
31309         Roo.each(xitems, this.addxtype, this);
31310     }
31311     
31312     
31313 };
31314 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31315     /**
31316      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31317      * @deprecated
31318      */
31319     endUpdate : function(){
31320         this.layout.endUpdate();
31321     },
31322
31323     /**
31324      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31325      *  @deprecated
31326      */
31327     beginUpdate : function(){
31328         this.layout.beginUpdate();
31329     },
31330
31331     /**
31332      * Get the BorderLayout for this dialog
31333      * @return {Roo.BorderLayout}
31334      */
31335     getLayout : function(){
31336         return this.layout;
31337     },
31338
31339     showEl : function(){
31340         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31341         if(Roo.isIE7){
31342             this.layout.layout();
31343         }
31344     },
31345
31346     // private
31347     // Use the syncHeightBeforeShow config option to control this automatically
31348     syncBodyHeight : function(){
31349         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31350         if(this.layout){this.layout.layout();}
31351     },
31352     
31353       /**
31354      * Add an xtype element (actually adds to the layout.)
31355      * @return {Object} xdata xtype object data.
31356      */
31357     
31358     addxtype : function(c) {
31359         return this.layout.addxtype(c);
31360     }
31361 });/*
31362  * Based on:
31363  * Ext JS Library 1.1.1
31364  * Copyright(c) 2006-2007, Ext JS, LLC.
31365  *
31366  * Originally Released Under LGPL - original licence link has changed is not relivant.
31367  *
31368  * Fork - LGPL
31369  * <script type="text/javascript">
31370  */
31371  
31372 /**
31373  * @class Roo.MessageBox
31374  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31375  * Example usage:
31376  *<pre><code>
31377 // Basic alert:
31378 Roo.Msg.alert('Status', 'Changes saved successfully.');
31379
31380 // Prompt for user data:
31381 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31382     if (btn == 'ok'){
31383         // process text value...
31384     }
31385 });
31386
31387 // Show a dialog using config options:
31388 Roo.Msg.show({
31389    title:'Save Changes?',
31390    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31391    buttons: Roo.Msg.YESNOCANCEL,
31392    fn: processResult,
31393    animEl: 'elId'
31394 });
31395 </code></pre>
31396  * @singleton
31397  */
31398 Roo.MessageBox = function(){
31399     var dlg, opt, mask, waitTimer;
31400     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31401     var buttons, activeTextEl, bwidth;
31402
31403     // private
31404     var handleButton = function(button){
31405         dlg.hide();
31406         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31407     };
31408
31409     // private
31410     var handleHide = function(){
31411         if(opt && opt.cls){
31412             dlg.el.removeClass(opt.cls);
31413         }
31414         if(waitTimer){
31415             Roo.TaskMgr.stop(waitTimer);
31416             waitTimer = null;
31417         }
31418     };
31419
31420     // private
31421     var updateButtons = function(b){
31422         var width = 0;
31423         if(!b){
31424             buttons["ok"].hide();
31425             buttons["cancel"].hide();
31426             buttons["yes"].hide();
31427             buttons["no"].hide();
31428             dlg.footer.dom.style.display = 'none';
31429             return width;
31430         }
31431         dlg.footer.dom.style.display = '';
31432         for(var k in buttons){
31433             if(typeof buttons[k] != "function"){
31434                 if(b[k]){
31435                     buttons[k].show();
31436                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31437                     width += buttons[k].el.getWidth()+15;
31438                 }else{
31439                     buttons[k].hide();
31440                 }
31441             }
31442         }
31443         return width;
31444     };
31445
31446     // private
31447     var handleEsc = function(d, k, e){
31448         if(opt && opt.closable !== false){
31449             dlg.hide();
31450         }
31451         if(e){
31452             e.stopEvent();
31453         }
31454     };
31455
31456     return {
31457         /**
31458          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31459          * @return {Roo.BasicDialog} The BasicDialog element
31460          */
31461         getDialog : function(){
31462            if(!dlg){
31463                 dlg = new Roo.BasicDialog("x-msg-box", {
31464                     autoCreate : true,
31465                     shadow: true,
31466                     draggable: true,
31467                     resizable:false,
31468                     constraintoviewport:false,
31469                     fixedcenter:true,
31470                     collapsible : false,
31471                     shim:true,
31472                     modal: true,
31473                     width:400, height:100,
31474                     buttonAlign:"center",
31475                     closeClick : function(){
31476                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31477                             handleButton("no");
31478                         }else{
31479                             handleButton("cancel");
31480                         }
31481                     }
31482                 });
31483                 dlg.on("hide", handleHide);
31484                 mask = dlg.mask;
31485                 dlg.addKeyListener(27, handleEsc);
31486                 buttons = {};
31487                 var bt = this.buttonText;
31488                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31489                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31490                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31491                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31492                 bodyEl = dlg.body.createChild({
31493
31494                     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>'
31495                 });
31496                 msgEl = bodyEl.dom.firstChild;
31497                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31498                 textboxEl.enableDisplayMode();
31499                 textboxEl.addKeyListener([10,13], function(){
31500                     if(dlg.isVisible() && opt && opt.buttons){
31501                         if(opt.buttons.ok){
31502                             handleButton("ok");
31503                         }else if(opt.buttons.yes){
31504                             handleButton("yes");
31505                         }
31506                     }
31507                 });
31508                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31509                 textareaEl.enableDisplayMode();
31510                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31511                 progressEl.enableDisplayMode();
31512                 var pf = progressEl.dom.firstChild;
31513                 if (pf) {
31514                     pp = Roo.get(pf.firstChild);
31515                     pp.setHeight(pf.offsetHeight);
31516                 }
31517                 
31518             }
31519             return dlg;
31520         },
31521
31522         /**
31523          * Updates the message box body text
31524          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31525          * the XHTML-compliant non-breaking space character '&amp;#160;')
31526          * @return {Roo.MessageBox} This message box
31527          */
31528         updateText : function(text){
31529             if(!dlg.isVisible() && !opt.width){
31530                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31531             }
31532             msgEl.innerHTML = text || '&#160;';
31533       
31534             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31535             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31536             var w = Math.max(
31537                     Math.min(opt.width || cw , this.maxWidth), 
31538                     Math.max(opt.minWidth || this.minWidth, bwidth)
31539             );
31540             if(opt.prompt){
31541                 activeTextEl.setWidth(w);
31542             }
31543             if(dlg.isVisible()){
31544                 dlg.fixedcenter = false;
31545             }
31546             // to big, make it scroll. = But as usual stupid IE does not support
31547             // !important..
31548             
31549             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31550                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31551                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31552             } else {
31553                 bodyEl.dom.style.height = '';
31554                 bodyEl.dom.style.overflowY = '';
31555             }
31556             if (cw > w) {
31557                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31558             } else {
31559                 bodyEl.dom.style.overflowX = '';
31560             }
31561             
31562             dlg.setContentSize(w, bodyEl.getHeight());
31563             if(dlg.isVisible()){
31564                 dlg.fixedcenter = true;
31565             }
31566             return this;
31567         },
31568
31569         /**
31570          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31571          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31572          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31573          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31574          * @return {Roo.MessageBox} This message box
31575          */
31576         updateProgress : function(value, text){
31577             if(text){
31578                 this.updateText(text);
31579             }
31580             if (pp) { // weird bug on my firefox - for some reason this is not defined
31581                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31582             }
31583             return this;
31584         },        
31585
31586         /**
31587          * Returns true if the message box is currently displayed
31588          * @return {Boolean} True if the message box is visible, else false
31589          */
31590         isVisible : function(){
31591             return dlg && dlg.isVisible();  
31592         },
31593
31594         /**
31595          * Hides the message box if it is displayed
31596          */
31597         hide : function(){
31598             if(this.isVisible()){
31599                 dlg.hide();
31600             }  
31601         },
31602
31603         /**
31604          * Displays a new message box, or reinitializes an existing message box, based on the config options
31605          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31606          * The following config object properties are supported:
31607          * <pre>
31608 Property    Type             Description
31609 ----------  ---------------  ------------------------------------------------------------------------------------
31610 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31611                                    closes (defaults to undefined)
31612 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31613                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31614 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31615                                    progress and wait dialogs will ignore this property and always hide the
31616                                    close button as they can only be closed programmatically.
31617 cls               String           A custom CSS class to apply to the message box element
31618 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31619                                    displayed (defaults to 75)
31620 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31621                                    function will be btn (the name of the button that was clicked, if applicable,
31622                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31623                                    Progress and wait dialogs will ignore this option since they do not respond to
31624                                    user actions and can only be closed programmatically, so any required function
31625                                    should be called by the same code after it closes the dialog.
31626 icon              String           A CSS class that provides a background image to be used as an icon for
31627                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31628 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31629 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31630 modal             Boolean          False to allow user interaction with the page while the message box is
31631                                    displayed (defaults to true)
31632 msg               String           A string that will replace the existing message box body text (defaults
31633                                    to the XHTML-compliant non-breaking space character '&#160;')
31634 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31635 progress          Boolean          True to display a progress bar (defaults to false)
31636 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31637 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31638 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31639 title             String           The title text
31640 value             String           The string value to set into the active textbox element if displayed
31641 wait              Boolean          True to display a progress bar (defaults to false)
31642 width             Number           The width of the dialog in pixels
31643 </pre>
31644          *
31645          * Example usage:
31646          * <pre><code>
31647 Roo.Msg.show({
31648    title: 'Address',
31649    msg: 'Please enter your address:',
31650    width: 300,
31651    buttons: Roo.MessageBox.OKCANCEL,
31652    multiline: true,
31653    fn: saveAddress,
31654    animEl: 'addAddressBtn'
31655 });
31656 </code></pre>
31657          * @param {Object} config Configuration options
31658          * @return {Roo.MessageBox} This message box
31659          */
31660         show : function(options)
31661         {
31662             
31663             // this causes nightmares if you show one dialog after another
31664             // especially on callbacks..
31665              
31666             if(this.isVisible()){
31667                 
31668                 this.hide();
31669                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31670                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31671                 Roo.log("New Dialog Message:" +  options.msg )
31672                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31673                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31674                 
31675             }
31676             var d = this.getDialog();
31677             opt = options;
31678             d.setTitle(opt.title || "&#160;");
31679             d.close.setDisplayed(opt.closable !== false);
31680             activeTextEl = textboxEl;
31681             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31682             if(opt.prompt){
31683                 if(opt.multiline){
31684                     textboxEl.hide();
31685                     textareaEl.show();
31686                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31687                         opt.multiline : this.defaultTextHeight);
31688                     activeTextEl = textareaEl;
31689                 }else{
31690                     textboxEl.show();
31691                     textareaEl.hide();
31692                 }
31693             }else{
31694                 textboxEl.hide();
31695                 textareaEl.hide();
31696             }
31697             progressEl.setDisplayed(opt.progress === true);
31698             this.updateProgress(0);
31699             activeTextEl.dom.value = opt.value || "";
31700             if(opt.prompt){
31701                 dlg.setDefaultButton(activeTextEl);
31702             }else{
31703                 var bs = opt.buttons;
31704                 var db = null;
31705                 if(bs && bs.ok){
31706                     db = buttons["ok"];
31707                 }else if(bs && bs.yes){
31708                     db = buttons["yes"];
31709                 }
31710                 dlg.setDefaultButton(db);
31711             }
31712             bwidth = updateButtons(opt.buttons);
31713             this.updateText(opt.msg);
31714             if(opt.cls){
31715                 d.el.addClass(opt.cls);
31716             }
31717             d.proxyDrag = opt.proxyDrag === true;
31718             d.modal = opt.modal !== false;
31719             d.mask = opt.modal !== false ? mask : false;
31720             if(!d.isVisible()){
31721                 // force it to the end of the z-index stack so it gets a cursor in FF
31722                 document.body.appendChild(dlg.el.dom);
31723                 d.animateTarget = null;
31724                 d.show(options.animEl);
31725             }
31726             return this;
31727         },
31728
31729         /**
31730          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31731          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31732          * and closing the message box when the process is complete.
31733          * @param {String} title The title bar text
31734          * @param {String} msg The message box body text
31735          * @return {Roo.MessageBox} This message box
31736          */
31737         progress : function(title, msg){
31738             this.show({
31739                 title : title,
31740                 msg : msg,
31741                 buttons: false,
31742                 progress:true,
31743                 closable:false,
31744                 minWidth: this.minProgressWidth,
31745                 modal : true
31746             });
31747             return this;
31748         },
31749
31750         /**
31751          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31752          * If a callback function is passed it will be called after the user clicks the button, and the
31753          * id of the button that was clicked will be passed as the only parameter to the callback
31754          * (could also be the top-right close button).
31755          * @param {String} title The title bar text
31756          * @param {String} msg The message box body text
31757          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31758          * @param {Object} scope (optional) The scope of the callback function
31759          * @return {Roo.MessageBox} This message box
31760          */
31761         alert : function(title, msg, fn, scope){
31762             this.show({
31763                 title : title,
31764                 msg : msg,
31765                 buttons: this.OK,
31766                 fn: fn,
31767                 scope : scope,
31768                 modal : true
31769             });
31770             return this;
31771         },
31772
31773         /**
31774          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31775          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31776          * You are responsible for closing the message box when the process is complete.
31777          * @param {String} msg The message box body text
31778          * @param {String} title (optional) The title bar text
31779          * @return {Roo.MessageBox} This message box
31780          */
31781         wait : function(msg, title){
31782             this.show({
31783                 title : title,
31784                 msg : msg,
31785                 buttons: false,
31786                 closable:false,
31787                 progress:true,
31788                 modal:true,
31789                 width:300,
31790                 wait:true
31791             });
31792             waitTimer = Roo.TaskMgr.start({
31793                 run: function(i){
31794                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31795                 },
31796                 interval: 1000
31797             });
31798             return this;
31799         },
31800
31801         /**
31802          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31803          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31804          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31805          * @param {String} title The title bar text
31806          * @param {String} msg The message box body text
31807          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31808          * @param {Object} scope (optional) The scope of the callback function
31809          * @return {Roo.MessageBox} This message box
31810          */
31811         confirm : function(title, msg, fn, scope){
31812             this.show({
31813                 title : title,
31814                 msg : msg,
31815                 buttons: this.YESNO,
31816                 fn: fn,
31817                 scope : scope,
31818                 modal : true
31819             });
31820             return this;
31821         },
31822
31823         /**
31824          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31825          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31826          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31827          * (could also be the top-right close button) and the text that was entered will be passed as the two
31828          * parameters to the callback.
31829          * @param {String} title The title bar text
31830          * @param {String} msg The message box body text
31831          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31832          * @param {Object} scope (optional) The scope of the callback function
31833          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31834          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31835          * @return {Roo.MessageBox} This message box
31836          */
31837         prompt : function(title, msg, fn, scope, multiline){
31838             this.show({
31839                 title : title,
31840                 msg : msg,
31841                 buttons: this.OKCANCEL,
31842                 fn: fn,
31843                 minWidth:250,
31844                 scope : scope,
31845                 prompt:true,
31846                 multiline: multiline,
31847                 modal : true
31848             });
31849             return this;
31850         },
31851
31852         /**
31853          * Button config that displays a single OK button
31854          * @type Object
31855          */
31856         OK : {ok:true},
31857         /**
31858          * Button config that displays Yes and No buttons
31859          * @type Object
31860          */
31861         YESNO : {yes:true, no:true},
31862         /**
31863          * Button config that displays OK and Cancel buttons
31864          * @type Object
31865          */
31866         OKCANCEL : {ok:true, cancel:true},
31867         /**
31868          * Button config that displays Yes, No and Cancel buttons
31869          * @type Object
31870          */
31871         YESNOCANCEL : {yes:true, no:true, cancel:true},
31872
31873         /**
31874          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31875          * @type Number
31876          */
31877         defaultTextHeight : 75,
31878         /**
31879          * The maximum width in pixels of the message box (defaults to 600)
31880          * @type Number
31881          */
31882         maxWidth : 600,
31883         /**
31884          * The minimum width in pixels of the message box (defaults to 100)
31885          * @type Number
31886          */
31887         minWidth : 100,
31888         /**
31889          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31890          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31891          * @type Number
31892          */
31893         minProgressWidth : 250,
31894         /**
31895          * An object containing the default button text strings that can be overriden for localized language support.
31896          * Supported properties are: ok, cancel, yes and no.
31897          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31898          * @type Object
31899          */
31900         buttonText : {
31901             ok : "OK",
31902             cancel : "Cancel",
31903             yes : "Yes",
31904             no : "No"
31905         }
31906     };
31907 }();
31908
31909 /**
31910  * Shorthand for {@link Roo.MessageBox}
31911  */
31912 Roo.Msg = Roo.MessageBox;/*
31913  * Based on:
31914  * Ext JS Library 1.1.1
31915  * Copyright(c) 2006-2007, Ext JS, LLC.
31916  *
31917  * Originally Released Under LGPL - original licence link has changed is not relivant.
31918  *
31919  * Fork - LGPL
31920  * <script type="text/javascript">
31921  */
31922 /**
31923  * @class Roo.QuickTips
31924  * Provides attractive and customizable tooltips for any element.
31925  * @singleton
31926  */
31927 Roo.QuickTips = function(){
31928     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31929     var ce, bd, xy, dd;
31930     var visible = false, disabled = true, inited = false;
31931     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31932     
31933     var onOver = function(e){
31934         if(disabled){
31935             return;
31936         }
31937         var t = e.getTarget();
31938         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31939             return;
31940         }
31941         if(ce && t == ce.el){
31942             clearTimeout(hideProc);
31943             return;
31944         }
31945         if(t && tagEls[t.id]){
31946             tagEls[t.id].el = t;
31947             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31948             return;
31949         }
31950         var ttp, et = Roo.fly(t);
31951         var ns = cfg.namespace;
31952         if(tm.interceptTitles && t.title){
31953             ttp = t.title;
31954             t.qtip = ttp;
31955             t.removeAttribute("title");
31956             e.preventDefault();
31957         }else{
31958             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31959         }
31960         if(ttp){
31961             showProc = show.defer(tm.showDelay, tm, [{
31962                 el: t, 
31963                 text: ttp, 
31964                 width: et.getAttributeNS(ns, cfg.width),
31965                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31966                 title: et.getAttributeNS(ns, cfg.title),
31967                     cls: et.getAttributeNS(ns, cfg.cls)
31968             }]);
31969         }
31970     };
31971     
31972     var onOut = function(e){
31973         clearTimeout(showProc);
31974         var t = e.getTarget();
31975         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31976             hideProc = setTimeout(hide, tm.hideDelay);
31977         }
31978     };
31979     
31980     var onMove = function(e){
31981         if(disabled){
31982             return;
31983         }
31984         xy = e.getXY();
31985         xy[1] += 18;
31986         if(tm.trackMouse && ce){
31987             el.setXY(xy);
31988         }
31989     };
31990     
31991     var onDown = function(e){
31992         clearTimeout(showProc);
31993         clearTimeout(hideProc);
31994         if(!e.within(el)){
31995             if(tm.hideOnClick){
31996                 hide();
31997                 tm.disable();
31998                 tm.enable.defer(100, tm);
31999             }
32000         }
32001     };
32002     
32003     var getPad = function(){
32004         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32005     };
32006
32007     var show = function(o){
32008         if(disabled){
32009             return;
32010         }
32011         clearTimeout(dismissProc);
32012         ce = o;
32013         if(removeCls){ // in case manually hidden
32014             el.removeClass(removeCls);
32015             removeCls = null;
32016         }
32017         if(ce.cls){
32018             el.addClass(ce.cls);
32019             removeCls = ce.cls;
32020         }
32021         if(ce.title){
32022             tipTitle.update(ce.title);
32023             tipTitle.show();
32024         }else{
32025             tipTitle.update('');
32026             tipTitle.hide();
32027         }
32028         el.dom.style.width  = tm.maxWidth+'px';
32029         //tipBody.dom.style.width = '';
32030         tipBodyText.update(o.text);
32031         var p = getPad(), w = ce.width;
32032         if(!w){
32033             var td = tipBodyText.dom;
32034             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32035             if(aw > tm.maxWidth){
32036                 w = tm.maxWidth;
32037             }else if(aw < tm.minWidth){
32038                 w = tm.minWidth;
32039             }else{
32040                 w = aw;
32041             }
32042         }
32043         //tipBody.setWidth(w);
32044         el.setWidth(parseInt(w, 10) + p);
32045         if(ce.autoHide === false){
32046             close.setDisplayed(true);
32047             if(dd){
32048                 dd.unlock();
32049             }
32050         }else{
32051             close.setDisplayed(false);
32052             if(dd){
32053                 dd.lock();
32054             }
32055         }
32056         if(xy){
32057             el.avoidY = xy[1]-18;
32058             el.setXY(xy);
32059         }
32060         if(tm.animate){
32061             el.setOpacity(.1);
32062             el.setStyle("visibility", "visible");
32063             el.fadeIn({callback: afterShow});
32064         }else{
32065             afterShow();
32066         }
32067     };
32068     
32069     var afterShow = function(){
32070         if(ce){
32071             el.show();
32072             esc.enable();
32073             if(tm.autoDismiss && ce.autoHide !== false){
32074                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32075             }
32076         }
32077     };
32078     
32079     var hide = function(noanim){
32080         clearTimeout(dismissProc);
32081         clearTimeout(hideProc);
32082         ce = null;
32083         if(el.isVisible()){
32084             esc.disable();
32085             if(noanim !== true && tm.animate){
32086                 el.fadeOut({callback: afterHide});
32087             }else{
32088                 afterHide();
32089             } 
32090         }
32091     };
32092     
32093     var afterHide = function(){
32094         el.hide();
32095         if(removeCls){
32096             el.removeClass(removeCls);
32097             removeCls = null;
32098         }
32099     };
32100     
32101     return {
32102         /**
32103         * @cfg {Number} minWidth
32104         * The minimum width of the quick tip (defaults to 40)
32105         */
32106        minWidth : 40,
32107         /**
32108         * @cfg {Number} maxWidth
32109         * The maximum width of the quick tip (defaults to 300)
32110         */
32111        maxWidth : 300,
32112         /**
32113         * @cfg {Boolean} interceptTitles
32114         * True to automatically use the element's DOM title value if available (defaults to false)
32115         */
32116        interceptTitles : false,
32117         /**
32118         * @cfg {Boolean} trackMouse
32119         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32120         */
32121        trackMouse : false,
32122         /**
32123         * @cfg {Boolean} hideOnClick
32124         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32125         */
32126        hideOnClick : true,
32127         /**
32128         * @cfg {Number} showDelay
32129         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32130         */
32131        showDelay : 500,
32132         /**
32133         * @cfg {Number} hideDelay
32134         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32135         */
32136        hideDelay : 200,
32137         /**
32138         * @cfg {Boolean} autoHide
32139         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32140         * Used in conjunction with hideDelay.
32141         */
32142        autoHide : true,
32143         /**
32144         * @cfg {Boolean}
32145         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32146         * (defaults to true).  Used in conjunction with autoDismissDelay.
32147         */
32148        autoDismiss : true,
32149         /**
32150         * @cfg {Number}
32151         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32152         */
32153        autoDismissDelay : 5000,
32154        /**
32155         * @cfg {Boolean} animate
32156         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32157         */
32158        animate : false,
32159
32160        /**
32161         * @cfg {String} title
32162         * Title text to display (defaults to '').  This can be any valid HTML markup.
32163         */
32164         title: '',
32165        /**
32166         * @cfg {String} text
32167         * Body text to display (defaults to '').  This can be any valid HTML markup.
32168         */
32169         text : '',
32170        /**
32171         * @cfg {String} cls
32172         * A CSS class to apply to the base quick tip element (defaults to '').
32173         */
32174         cls : '',
32175        /**
32176         * @cfg {Number} width
32177         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32178         * minWidth or maxWidth.
32179         */
32180         width : null,
32181
32182     /**
32183      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32184      * or display QuickTips in a page.
32185      */
32186        init : function(){
32187           tm = Roo.QuickTips;
32188           cfg = tm.tagConfig;
32189           if(!inited){
32190               if(!Roo.isReady){ // allow calling of init() before onReady
32191                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32192                   return;
32193               }
32194               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32195               el.fxDefaults = {stopFx: true};
32196               // maximum custom styling
32197               //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>');
32198               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>');              
32199               tipTitle = el.child('h3');
32200               tipTitle.enableDisplayMode("block");
32201               tipBody = el.child('div.x-tip-bd');
32202               tipBodyText = el.child('div.x-tip-bd-inner');
32203               //bdLeft = el.child('div.x-tip-bd-left');
32204               //bdRight = el.child('div.x-tip-bd-right');
32205               close = el.child('div.x-tip-close');
32206               close.enableDisplayMode("block");
32207               close.on("click", hide);
32208               var d = Roo.get(document);
32209               d.on("mousedown", onDown);
32210               d.on("mouseover", onOver);
32211               d.on("mouseout", onOut);
32212               d.on("mousemove", onMove);
32213               esc = d.addKeyListener(27, hide);
32214               esc.disable();
32215               if(Roo.dd.DD){
32216                   dd = el.initDD("default", null, {
32217                       onDrag : function(){
32218                           el.sync();  
32219                       }
32220                   });
32221                   dd.setHandleElId(tipTitle.id);
32222                   dd.lock();
32223               }
32224               inited = true;
32225           }
32226           this.enable(); 
32227        },
32228
32229     /**
32230      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32231      * are supported:
32232      * <pre>
32233 Property    Type                   Description
32234 ----------  ---------------------  ------------------------------------------------------------------------
32235 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32236      * </ul>
32237      * @param {Object} config The config object
32238      */
32239        register : function(config){
32240            var cs = config instanceof Array ? config : arguments;
32241            for(var i = 0, len = cs.length; i < len; i++) {
32242                var c = cs[i];
32243                var target = c.target;
32244                if(target){
32245                    if(target instanceof Array){
32246                        for(var j = 0, jlen = target.length; j < jlen; j++){
32247                            tagEls[target[j]] = c;
32248                        }
32249                    }else{
32250                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32251                    }
32252                }
32253            }
32254        },
32255
32256     /**
32257      * Removes this quick tip from its element and destroys it.
32258      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32259      */
32260        unregister : function(el){
32261            delete tagEls[Roo.id(el)];
32262        },
32263
32264     /**
32265      * Enable this quick tip.
32266      */
32267        enable : function(){
32268            if(inited && disabled){
32269                locks.pop();
32270                if(locks.length < 1){
32271                    disabled = false;
32272                }
32273            }
32274        },
32275
32276     /**
32277      * Disable this quick tip.
32278      */
32279        disable : function(){
32280           disabled = true;
32281           clearTimeout(showProc);
32282           clearTimeout(hideProc);
32283           clearTimeout(dismissProc);
32284           if(ce){
32285               hide(true);
32286           }
32287           locks.push(1);
32288        },
32289
32290     /**
32291      * Returns true if the quick tip is enabled, else false.
32292      */
32293        isEnabled : function(){
32294             return !disabled;
32295        },
32296
32297         // private
32298        tagConfig : {
32299            namespace : "ext",
32300            attribute : "qtip",
32301            width : "width",
32302            target : "target",
32303            title : "qtitle",
32304            hide : "hide",
32305            cls : "qclass"
32306        }
32307    };
32308 }();
32309
32310 // backwards compat
32311 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32312  * Based on:
32313  * Ext JS Library 1.1.1
32314  * Copyright(c) 2006-2007, Ext JS, LLC.
32315  *
32316  * Originally Released Under LGPL - original licence link has changed is not relivant.
32317  *
32318  * Fork - LGPL
32319  * <script type="text/javascript">
32320  */
32321  
32322
32323 /**
32324  * @class Roo.tree.TreePanel
32325  * @extends Roo.data.Tree
32326
32327  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32328  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32329  * @cfg {Boolean} enableDD true to enable drag and drop
32330  * @cfg {Boolean} enableDrag true to enable just drag
32331  * @cfg {Boolean} enableDrop true to enable just drop
32332  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32333  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32334  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32335  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32336  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32337  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32338  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32339  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32340  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32341  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32342  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32343  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32344  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32345  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32346  * @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>
32347  * @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>
32348  * 
32349  * @constructor
32350  * @param {String/HTMLElement/Element} el The container element
32351  * @param {Object} config
32352  */
32353 Roo.tree.TreePanel = function(el, config){
32354     var root = false;
32355     var loader = false;
32356     if (config.root) {
32357         root = config.root;
32358         delete config.root;
32359     }
32360     if (config.loader) {
32361         loader = config.loader;
32362         delete config.loader;
32363     }
32364     
32365     Roo.apply(this, config);
32366     Roo.tree.TreePanel.superclass.constructor.call(this);
32367     this.el = Roo.get(el);
32368     this.el.addClass('x-tree');
32369     //console.log(root);
32370     if (root) {
32371         this.setRootNode( Roo.factory(root, Roo.tree));
32372     }
32373     if (loader) {
32374         this.loader = Roo.factory(loader, Roo.tree);
32375     }
32376    /**
32377     * Read-only. The id of the container element becomes this TreePanel's id.
32378     */
32379     this.id = this.el.id;
32380     this.addEvents({
32381         /**
32382         * @event beforeload
32383         * Fires before a node is loaded, return false to cancel
32384         * @param {Node} node The node being loaded
32385         */
32386         "beforeload" : true,
32387         /**
32388         * @event load
32389         * Fires when a node is loaded
32390         * @param {Node} node The node that was loaded
32391         */
32392         "load" : true,
32393         /**
32394         * @event textchange
32395         * Fires when the text for a node is changed
32396         * @param {Node} node The node
32397         * @param {String} text The new text
32398         * @param {String} oldText The old text
32399         */
32400         "textchange" : true,
32401         /**
32402         * @event beforeexpand
32403         * Fires before a node is expanded, return false to cancel.
32404         * @param {Node} node The node
32405         * @param {Boolean} deep
32406         * @param {Boolean} anim
32407         */
32408         "beforeexpand" : true,
32409         /**
32410         * @event beforecollapse
32411         * Fires before a node is collapsed, return false to cancel.
32412         * @param {Node} node The node
32413         * @param {Boolean} deep
32414         * @param {Boolean} anim
32415         */
32416         "beforecollapse" : true,
32417         /**
32418         * @event expand
32419         * Fires when a node is expanded
32420         * @param {Node} node The node
32421         */
32422         "expand" : true,
32423         /**
32424         * @event disabledchange
32425         * Fires when the disabled status of a node changes
32426         * @param {Node} node The node
32427         * @param {Boolean} disabled
32428         */
32429         "disabledchange" : true,
32430         /**
32431         * @event collapse
32432         * Fires when a node is collapsed
32433         * @param {Node} node The node
32434         */
32435         "collapse" : true,
32436         /**
32437         * @event beforeclick
32438         * Fires before click processing on a node. Return false to cancel the default action.
32439         * @param {Node} node The node
32440         * @param {Roo.EventObject} e The event object
32441         */
32442         "beforeclick":true,
32443         /**
32444         * @event checkchange
32445         * Fires when a node with a checkbox's checked property changes
32446         * @param {Node} this This node
32447         * @param {Boolean} checked
32448         */
32449         "checkchange":true,
32450         /**
32451         * @event click
32452         * Fires when a node is clicked
32453         * @param {Node} node The node
32454         * @param {Roo.EventObject} e The event object
32455         */
32456         "click":true,
32457         /**
32458         * @event dblclick
32459         * Fires when a node is double clicked
32460         * @param {Node} node The node
32461         * @param {Roo.EventObject} e The event object
32462         */
32463         "dblclick":true,
32464         /**
32465         * @event contextmenu
32466         * Fires when a node is right clicked
32467         * @param {Node} node The node
32468         * @param {Roo.EventObject} e The event object
32469         */
32470         "contextmenu":true,
32471         /**
32472         * @event beforechildrenrendered
32473         * Fires right before the child nodes for a node are rendered
32474         * @param {Node} node The node
32475         */
32476         "beforechildrenrendered":true,
32477         /**
32478         * @event startdrag
32479         * Fires when a node starts being dragged
32480         * @param {Roo.tree.TreePanel} this
32481         * @param {Roo.tree.TreeNode} node
32482         * @param {event} e The raw browser event
32483         */ 
32484        "startdrag" : true,
32485        /**
32486         * @event enddrag
32487         * Fires when a drag operation is complete
32488         * @param {Roo.tree.TreePanel} this
32489         * @param {Roo.tree.TreeNode} node
32490         * @param {event} e The raw browser event
32491         */
32492        "enddrag" : true,
32493        /**
32494         * @event dragdrop
32495         * Fires when a dragged node is dropped on a valid DD target
32496         * @param {Roo.tree.TreePanel} this
32497         * @param {Roo.tree.TreeNode} node
32498         * @param {DD} dd The dd it was dropped on
32499         * @param {event} e The raw browser event
32500         */
32501        "dragdrop" : true,
32502        /**
32503         * @event beforenodedrop
32504         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32505         * passed to handlers has the following properties:<br />
32506         * <ul style="padding:5px;padding-left:16px;">
32507         * <li>tree - The TreePanel</li>
32508         * <li>target - The node being targeted for the drop</li>
32509         * <li>data - The drag data from the drag source</li>
32510         * <li>point - The point of the drop - append, above or below</li>
32511         * <li>source - The drag source</li>
32512         * <li>rawEvent - Raw mouse event</li>
32513         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32514         * to be inserted by setting them on this object.</li>
32515         * <li>cancel - Set this to true to cancel the drop.</li>
32516         * </ul>
32517         * @param {Object} dropEvent
32518         */
32519        "beforenodedrop" : true,
32520        /**
32521         * @event nodedrop
32522         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32523         * passed to handlers has the following properties:<br />
32524         * <ul style="padding:5px;padding-left:16px;">
32525         * <li>tree - The TreePanel</li>
32526         * <li>target - The node being targeted for the drop</li>
32527         * <li>data - The drag data from the drag source</li>
32528         * <li>point - The point of the drop - append, above or below</li>
32529         * <li>source - The drag source</li>
32530         * <li>rawEvent - Raw mouse event</li>
32531         * <li>dropNode - Dropped node(s).</li>
32532         * </ul>
32533         * @param {Object} dropEvent
32534         */
32535        "nodedrop" : true,
32536         /**
32537         * @event nodedragover
32538         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32539         * passed to handlers has the following properties:<br />
32540         * <ul style="padding:5px;padding-left:16px;">
32541         * <li>tree - The TreePanel</li>
32542         * <li>target - The node being targeted for the drop</li>
32543         * <li>data - The drag data from the drag source</li>
32544         * <li>point - The point of the drop - append, above or below</li>
32545         * <li>source - The drag source</li>
32546         * <li>rawEvent - Raw mouse event</li>
32547         * <li>dropNode - Drop node(s) provided by the source.</li>
32548         * <li>cancel - Set this to true to signal drop not allowed.</li>
32549         * </ul>
32550         * @param {Object} dragOverEvent
32551         */
32552        "nodedragover" : true
32553         
32554     });
32555     if(this.singleExpand){
32556        this.on("beforeexpand", this.restrictExpand, this);
32557     }
32558     if (this.editor) {
32559         this.editor.tree = this;
32560         this.editor = Roo.factory(this.editor, Roo.tree);
32561     }
32562     
32563     if (this.selModel) {
32564         this.selModel = Roo.factory(this.selModel, Roo.tree);
32565     }
32566    
32567 };
32568 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32569     rootVisible : true,
32570     animate: Roo.enableFx,
32571     lines : true,
32572     enableDD : false,
32573     hlDrop : Roo.enableFx,
32574   
32575     renderer: false,
32576     
32577     rendererTip: false,
32578     // private
32579     restrictExpand : function(node){
32580         var p = node.parentNode;
32581         if(p){
32582             if(p.expandedChild && p.expandedChild.parentNode == p){
32583                 p.expandedChild.collapse();
32584             }
32585             p.expandedChild = node;
32586         }
32587     },
32588
32589     // private override
32590     setRootNode : function(node){
32591         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32592         if(!this.rootVisible){
32593             node.ui = new Roo.tree.RootTreeNodeUI(node);
32594         }
32595         return node;
32596     },
32597
32598     /**
32599      * Returns the container element for this TreePanel
32600      */
32601     getEl : function(){
32602         return this.el;
32603     },
32604
32605     /**
32606      * Returns the default TreeLoader for this TreePanel
32607      */
32608     getLoader : function(){
32609         return this.loader;
32610     },
32611
32612     /**
32613      * Expand all nodes
32614      */
32615     expandAll : function(){
32616         this.root.expand(true);
32617     },
32618
32619     /**
32620      * Collapse all nodes
32621      */
32622     collapseAll : function(){
32623         this.root.collapse(true);
32624     },
32625
32626     /**
32627      * Returns the selection model used by this TreePanel
32628      */
32629     getSelectionModel : function(){
32630         if(!this.selModel){
32631             this.selModel = new Roo.tree.DefaultSelectionModel();
32632         }
32633         return this.selModel;
32634     },
32635
32636     /**
32637      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32638      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32639      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32640      * @return {Array}
32641      */
32642     getChecked : function(a, startNode){
32643         startNode = startNode || this.root;
32644         var r = [];
32645         var f = function(){
32646             if(this.attributes.checked){
32647                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32648             }
32649         }
32650         startNode.cascade(f);
32651         return r;
32652     },
32653
32654     /**
32655      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32656      * @param {String} path
32657      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32658      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32659      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32660      */
32661     expandPath : function(path, attr, callback){
32662         attr = attr || "id";
32663         var keys = path.split(this.pathSeparator);
32664         var curNode = this.root;
32665         if(curNode.attributes[attr] != keys[1]){ // invalid root
32666             if(callback){
32667                 callback(false, null);
32668             }
32669             return;
32670         }
32671         var index = 1;
32672         var f = function(){
32673             if(++index == keys.length){
32674                 if(callback){
32675                     callback(true, curNode);
32676                 }
32677                 return;
32678             }
32679             var c = curNode.findChild(attr, keys[index]);
32680             if(!c){
32681                 if(callback){
32682                     callback(false, curNode);
32683                 }
32684                 return;
32685             }
32686             curNode = c;
32687             c.expand(false, false, f);
32688         };
32689         curNode.expand(false, false, f);
32690     },
32691
32692     /**
32693      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32694      * @param {String} path
32695      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32696      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32697      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32698      */
32699     selectPath : function(path, attr, callback){
32700         attr = attr || "id";
32701         var keys = path.split(this.pathSeparator);
32702         var v = keys.pop();
32703         if(keys.length > 0){
32704             var f = function(success, node){
32705                 if(success && node){
32706                     var n = node.findChild(attr, v);
32707                     if(n){
32708                         n.select();
32709                         if(callback){
32710                             callback(true, n);
32711                         }
32712                     }else if(callback){
32713                         callback(false, n);
32714                     }
32715                 }else{
32716                     if(callback){
32717                         callback(false, n);
32718                     }
32719                 }
32720             };
32721             this.expandPath(keys.join(this.pathSeparator), attr, f);
32722         }else{
32723             this.root.select();
32724             if(callback){
32725                 callback(true, this.root);
32726             }
32727         }
32728     },
32729
32730     getTreeEl : function(){
32731         return this.el;
32732     },
32733
32734     /**
32735      * Trigger rendering of this TreePanel
32736      */
32737     render : function(){
32738         if (this.innerCt) {
32739             return this; // stop it rendering more than once!!
32740         }
32741         
32742         this.innerCt = this.el.createChild({tag:"ul",
32743                cls:"x-tree-root-ct " +
32744                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32745
32746         if(this.containerScroll){
32747             Roo.dd.ScrollManager.register(this.el);
32748         }
32749         if((this.enableDD || this.enableDrop) && !this.dropZone){
32750            /**
32751             * The dropZone used by this tree if drop is enabled
32752             * @type Roo.tree.TreeDropZone
32753             */
32754              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32755                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32756            });
32757         }
32758         if((this.enableDD || this.enableDrag) && !this.dragZone){
32759            /**
32760             * The dragZone used by this tree if drag is enabled
32761             * @type Roo.tree.TreeDragZone
32762             */
32763             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32764                ddGroup: this.ddGroup || "TreeDD",
32765                scroll: this.ddScroll
32766            });
32767         }
32768         this.getSelectionModel().init(this);
32769         if (!this.root) {
32770             Roo.log("ROOT not set in tree");
32771             return this;
32772         }
32773         this.root.render();
32774         if(!this.rootVisible){
32775             this.root.renderChildren();
32776         }
32777         return this;
32778     }
32779 });/*
32780  * Based on:
32781  * Ext JS Library 1.1.1
32782  * Copyright(c) 2006-2007, Ext JS, LLC.
32783  *
32784  * Originally Released Under LGPL - original licence link has changed is not relivant.
32785  *
32786  * Fork - LGPL
32787  * <script type="text/javascript">
32788  */
32789  
32790
32791 /**
32792  * @class Roo.tree.DefaultSelectionModel
32793  * @extends Roo.util.Observable
32794  * The default single selection for a TreePanel.
32795  * @param {Object} cfg Configuration
32796  */
32797 Roo.tree.DefaultSelectionModel = function(cfg){
32798    this.selNode = null;
32799    
32800    
32801    
32802    this.addEvents({
32803        /**
32804         * @event selectionchange
32805         * Fires when the selected node changes
32806         * @param {DefaultSelectionModel} this
32807         * @param {TreeNode} node the new selection
32808         */
32809        "selectionchange" : true,
32810
32811        /**
32812         * @event beforeselect
32813         * Fires before the selected node changes, return false to cancel the change
32814         * @param {DefaultSelectionModel} this
32815         * @param {TreeNode} node the new selection
32816         * @param {TreeNode} node the old selection
32817         */
32818        "beforeselect" : true
32819    });
32820    
32821     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32822 };
32823
32824 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32825     init : function(tree){
32826         this.tree = tree;
32827         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32828         tree.on("click", this.onNodeClick, this);
32829     },
32830     
32831     onNodeClick : function(node, e){
32832         if (e.ctrlKey && this.selNode == node)  {
32833             this.unselect(node);
32834             return;
32835         }
32836         this.select(node);
32837     },
32838     
32839     /**
32840      * Select a node.
32841      * @param {TreeNode} node The node to select
32842      * @return {TreeNode} The selected node
32843      */
32844     select : function(node){
32845         var last = this.selNode;
32846         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32847             if(last){
32848                 last.ui.onSelectedChange(false);
32849             }
32850             this.selNode = node;
32851             node.ui.onSelectedChange(true);
32852             this.fireEvent("selectionchange", this, node, last);
32853         }
32854         return node;
32855     },
32856     
32857     /**
32858      * Deselect a node.
32859      * @param {TreeNode} node The node to unselect
32860      */
32861     unselect : function(node){
32862         if(this.selNode == node){
32863             this.clearSelections();
32864         }    
32865     },
32866     
32867     /**
32868      * Clear all selections
32869      */
32870     clearSelections : function(){
32871         var n = this.selNode;
32872         if(n){
32873             n.ui.onSelectedChange(false);
32874             this.selNode = null;
32875             this.fireEvent("selectionchange", this, null);
32876         }
32877         return n;
32878     },
32879     
32880     /**
32881      * Get the selected node
32882      * @return {TreeNode} The selected node
32883      */
32884     getSelectedNode : function(){
32885         return this.selNode;    
32886     },
32887     
32888     /**
32889      * Returns true if the node is selected
32890      * @param {TreeNode} node The node to check
32891      * @return {Boolean}
32892      */
32893     isSelected : function(node){
32894         return this.selNode == node;  
32895     },
32896
32897     /**
32898      * Selects the node above the selected node in the tree, intelligently walking the nodes
32899      * @return TreeNode The new selection
32900      */
32901     selectPrevious : function(){
32902         var s = this.selNode || this.lastSelNode;
32903         if(!s){
32904             return null;
32905         }
32906         var ps = s.previousSibling;
32907         if(ps){
32908             if(!ps.isExpanded() || ps.childNodes.length < 1){
32909                 return this.select(ps);
32910             } else{
32911                 var lc = ps.lastChild;
32912                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32913                     lc = lc.lastChild;
32914                 }
32915                 return this.select(lc);
32916             }
32917         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32918             return this.select(s.parentNode);
32919         }
32920         return null;
32921     },
32922
32923     /**
32924      * Selects the node above the selected node in the tree, intelligently walking the nodes
32925      * @return TreeNode The new selection
32926      */
32927     selectNext : function(){
32928         var s = this.selNode || this.lastSelNode;
32929         if(!s){
32930             return null;
32931         }
32932         if(s.firstChild && s.isExpanded()){
32933              return this.select(s.firstChild);
32934          }else if(s.nextSibling){
32935              return this.select(s.nextSibling);
32936          }else if(s.parentNode){
32937             var newS = null;
32938             s.parentNode.bubble(function(){
32939                 if(this.nextSibling){
32940                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32941                     return false;
32942                 }
32943             });
32944             return newS;
32945          }
32946         return null;
32947     },
32948
32949     onKeyDown : function(e){
32950         var s = this.selNode || this.lastSelNode;
32951         // undesirable, but required
32952         var sm = this;
32953         if(!s){
32954             return;
32955         }
32956         var k = e.getKey();
32957         switch(k){
32958              case e.DOWN:
32959                  e.stopEvent();
32960                  this.selectNext();
32961              break;
32962              case e.UP:
32963                  e.stopEvent();
32964                  this.selectPrevious();
32965              break;
32966              case e.RIGHT:
32967                  e.preventDefault();
32968                  if(s.hasChildNodes()){
32969                      if(!s.isExpanded()){
32970                          s.expand();
32971                      }else if(s.firstChild){
32972                          this.select(s.firstChild, e);
32973                      }
32974                  }
32975              break;
32976              case e.LEFT:
32977                  e.preventDefault();
32978                  if(s.hasChildNodes() && s.isExpanded()){
32979                      s.collapse();
32980                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32981                      this.select(s.parentNode, e);
32982                  }
32983              break;
32984         };
32985     }
32986 });
32987
32988 /**
32989  * @class Roo.tree.MultiSelectionModel
32990  * @extends Roo.util.Observable
32991  * Multi selection for a TreePanel.
32992  * @param {Object} cfg Configuration
32993  */
32994 Roo.tree.MultiSelectionModel = function(){
32995    this.selNodes = [];
32996    this.selMap = {};
32997    this.addEvents({
32998        /**
32999         * @event selectionchange
33000         * Fires when the selected nodes change
33001         * @param {MultiSelectionModel} this
33002         * @param {Array} nodes Array of the selected nodes
33003         */
33004        "selectionchange" : true
33005    });
33006    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33007    
33008 };
33009
33010 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33011     init : function(tree){
33012         this.tree = tree;
33013         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33014         tree.on("click", this.onNodeClick, this);
33015     },
33016     
33017     onNodeClick : function(node, e){
33018         this.select(node, e, e.ctrlKey);
33019     },
33020     
33021     /**
33022      * Select a node.
33023      * @param {TreeNode} node The node to select
33024      * @param {EventObject} e (optional) An event associated with the selection
33025      * @param {Boolean} keepExisting True to retain existing selections
33026      * @return {TreeNode} The selected node
33027      */
33028     select : function(node, e, keepExisting){
33029         if(keepExisting !== true){
33030             this.clearSelections(true);
33031         }
33032         if(this.isSelected(node)){
33033             this.lastSelNode = node;
33034             return node;
33035         }
33036         this.selNodes.push(node);
33037         this.selMap[node.id] = node;
33038         this.lastSelNode = node;
33039         node.ui.onSelectedChange(true);
33040         this.fireEvent("selectionchange", this, this.selNodes);
33041         return node;
33042     },
33043     
33044     /**
33045      * Deselect a node.
33046      * @param {TreeNode} node The node to unselect
33047      */
33048     unselect : function(node){
33049         if(this.selMap[node.id]){
33050             node.ui.onSelectedChange(false);
33051             var sn = this.selNodes;
33052             var index = -1;
33053             if(sn.indexOf){
33054                 index = sn.indexOf(node);
33055             }else{
33056                 for(var i = 0, len = sn.length; i < len; i++){
33057                     if(sn[i] == node){
33058                         index = i;
33059                         break;
33060                     }
33061                 }
33062             }
33063             if(index != -1){
33064                 this.selNodes.splice(index, 1);
33065             }
33066             delete this.selMap[node.id];
33067             this.fireEvent("selectionchange", this, this.selNodes);
33068         }
33069     },
33070     
33071     /**
33072      * Clear all selections
33073      */
33074     clearSelections : function(suppressEvent){
33075         var sn = this.selNodes;
33076         if(sn.length > 0){
33077             for(var i = 0, len = sn.length; i < len; i++){
33078                 sn[i].ui.onSelectedChange(false);
33079             }
33080             this.selNodes = [];
33081             this.selMap = {};
33082             if(suppressEvent !== true){
33083                 this.fireEvent("selectionchange", this, this.selNodes);
33084             }
33085         }
33086     },
33087     
33088     /**
33089      * Returns true if the node is selected
33090      * @param {TreeNode} node The node to check
33091      * @return {Boolean}
33092      */
33093     isSelected : function(node){
33094         return this.selMap[node.id] ? true : false;  
33095     },
33096     
33097     /**
33098      * Returns an array of the selected nodes
33099      * @return {Array}
33100      */
33101     getSelectedNodes : function(){
33102         return this.selNodes;    
33103     },
33104
33105     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33106
33107     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33108
33109     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33110 });/*
33111  * Based on:
33112  * Ext JS Library 1.1.1
33113  * Copyright(c) 2006-2007, Ext JS, LLC.
33114  *
33115  * Originally Released Under LGPL - original licence link has changed is not relivant.
33116  *
33117  * Fork - LGPL
33118  * <script type="text/javascript">
33119  */
33120  
33121 /**
33122  * @class Roo.tree.TreeNode
33123  * @extends Roo.data.Node
33124  * @cfg {String} text The text for this node
33125  * @cfg {Boolean} expanded true to start the node expanded
33126  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33127  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33128  * @cfg {Boolean} disabled true to start the node disabled
33129  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33130  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33131  * @cfg {String} cls A css class to be added to the node
33132  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33133  * @cfg {String} href URL of the link used for the node (defaults to #)
33134  * @cfg {String} hrefTarget target frame for the link
33135  * @cfg {String} qtip An Ext QuickTip for the node
33136  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33137  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33138  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33139  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33140  * (defaults to undefined with no checkbox rendered)
33141  * @constructor
33142  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33143  */
33144 Roo.tree.TreeNode = function(attributes){
33145     attributes = attributes || {};
33146     if(typeof attributes == "string"){
33147         attributes = {text: attributes};
33148     }
33149     this.childrenRendered = false;
33150     this.rendered = false;
33151     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33152     this.expanded = attributes.expanded === true;
33153     this.isTarget = attributes.isTarget !== false;
33154     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33155     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33156
33157     /**
33158      * Read-only. The text for this node. To change it use setText().
33159      * @type String
33160      */
33161     this.text = attributes.text;
33162     /**
33163      * True if this node is disabled.
33164      * @type Boolean
33165      */
33166     this.disabled = attributes.disabled === true;
33167
33168     this.addEvents({
33169         /**
33170         * @event textchange
33171         * Fires when the text for this node is changed
33172         * @param {Node} this This node
33173         * @param {String} text The new text
33174         * @param {String} oldText The old text
33175         */
33176         "textchange" : true,
33177         /**
33178         * @event beforeexpand
33179         * Fires before this node is expanded, return false to cancel.
33180         * @param {Node} this This node
33181         * @param {Boolean} deep
33182         * @param {Boolean} anim
33183         */
33184         "beforeexpand" : true,
33185         /**
33186         * @event beforecollapse
33187         * Fires before this node is collapsed, return false to cancel.
33188         * @param {Node} this This node
33189         * @param {Boolean} deep
33190         * @param {Boolean} anim
33191         */
33192         "beforecollapse" : true,
33193         /**
33194         * @event expand
33195         * Fires when this node is expanded
33196         * @param {Node} this This node
33197         */
33198         "expand" : true,
33199         /**
33200         * @event disabledchange
33201         * Fires when the disabled status of this node changes
33202         * @param {Node} this This node
33203         * @param {Boolean} disabled
33204         */
33205         "disabledchange" : true,
33206         /**
33207         * @event collapse
33208         * Fires when this node is collapsed
33209         * @param {Node} this This node
33210         */
33211         "collapse" : true,
33212         /**
33213         * @event beforeclick
33214         * Fires before click processing. Return false to cancel the default action.
33215         * @param {Node} this This node
33216         * @param {Roo.EventObject} e The event object
33217         */
33218         "beforeclick":true,
33219         /**
33220         * @event checkchange
33221         * Fires when a node with a checkbox's checked property changes
33222         * @param {Node} this This node
33223         * @param {Boolean} checked
33224         */
33225         "checkchange":true,
33226         /**
33227         * @event click
33228         * Fires when this node is clicked
33229         * @param {Node} this This node
33230         * @param {Roo.EventObject} e The event object
33231         */
33232         "click":true,
33233         /**
33234         * @event dblclick
33235         * Fires when this node is double clicked
33236         * @param {Node} this This node
33237         * @param {Roo.EventObject} e The event object
33238         */
33239         "dblclick":true,
33240         /**
33241         * @event contextmenu
33242         * Fires when this node is right clicked
33243         * @param {Node} this This node
33244         * @param {Roo.EventObject} e The event object
33245         */
33246         "contextmenu":true,
33247         /**
33248         * @event beforechildrenrendered
33249         * Fires right before the child nodes for this node are rendered
33250         * @param {Node} this This node
33251         */
33252         "beforechildrenrendered":true
33253     });
33254
33255     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33256
33257     /**
33258      * Read-only. The UI for this node
33259      * @type TreeNodeUI
33260      */
33261     this.ui = new uiClass(this);
33262     
33263     // finally support items[]
33264     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33265         return;
33266     }
33267     
33268     
33269     Roo.each(this.attributes.items, function(c) {
33270         this.appendChild(Roo.factory(c,Roo.Tree));
33271     }, this);
33272     delete this.attributes.items;
33273     
33274     
33275     
33276 };
33277 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33278     preventHScroll: true,
33279     /**
33280      * Returns true if this node is expanded
33281      * @return {Boolean}
33282      */
33283     isExpanded : function(){
33284         return this.expanded;
33285     },
33286
33287     /**
33288      * Returns the UI object for this node
33289      * @return {TreeNodeUI}
33290      */
33291     getUI : function(){
33292         return this.ui;
33293     },
33294
33295     // private override
33296     setFirstChild : function(node){
33297         var of = this.firstChild;
33298         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33299         if(this.childrenRendered && of && node != of){
33300             of.renderIndent(true, true);
33301         }
33302         if(this.rendered){
33303             this.renderIndent(true, true);
33304         }
33305     },
33306
33307     // private override
33308     setLastChild : function(node){
33309         var ol = this.lastChild;
33310         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33311         if(this.childrenRendered && ol && node != ol){
33312             ol.renderIndent(true, true);
33313         }
33314         if(this.rendered){
33315             this.renderIndent(true, true);
33316         }
33317     },
33318
33319     // these methods are overridden to provide lazy rendering support
33320     // private override
33321     appendChild : function()
33322     {
33323         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33324         if(node && this.childrenRendered){
33325             node.render();
33326         }
33327         this.ui.updateExpandIcon();
33328         return node;
33329     },
33330
33331     // private override
33332     removeChild : function(node){
33333         this.ownerTree.getSelectionModel().unselect(node);
33334         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33335         // if it's been rendered remove dom node
33336         if(this.childrenRendered){
33337             node.ui.remove();
33338         }
33339         if(this.childNodes.length < 1){
33340             this.collapse(false, false);
33341         }else{
33342             this.ui.updateExpandIcon();
33343         }
33344         if(!this.firstChild) {
33345             this.childrenRendered = false;
33346         }
33347         return node;
33348     },
33349
33350     // private override
33351     insertBefore : function(node, refNode){
33352         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33353         if(newNode && refNode && this.childrenRendered){
33354             node.render();
33355         }
33356         this.ui.updateExpandIcon();
33357         return newNode;
33358     },
33359
33360     /**
33361      * Sets the text for this node
33362      * @param {String} text
33363      */
33364     setText : function(text){
33365         var oldText = this.text;
33366         this.text = text;
33367         this.attributes.text = text;
33368         if(this.rendered){ // event without subscribing
33369             this.ui.onTextChange(this, text, oldText);
33370         }
33371         this.fireEvent("textchange", this, text, oldText);
33372     },
33373
33374     /**
33375      * Triggers selection of this node
33376      */
33377     select : function(){
33378         this.getOwnerTree().getSelectionModel().select(this);
33379     },
33380
33381     /**
33382      * Triggers deselection of this node
33383      */
33384     unselect : function(){
33385         this.getOwnerTree().getSelectionModel().unselect(this);
33386     },
33387
33388     /**
33389      * Returns true if this node is selected
33390      * @return {Boolean}
33391      */
33392     isSelected : function(){
33393         return this.getOwnerTree().getSelectionModel().isSelected(this);
33394     },
33395
33396     /**
33397      * Expand this node.
33398      * @param {Boolean} deep (optional) True to expand all children as well
33399      * @param {Boolean} anim (optional) false to cancel the default animation
33400      * @param {Function} callback (optional) A callback to be called when
33401      * expanding this node completes (does not wait for deep expand to complete).
33402      * Called with 1 parameter, this node.
33403      */
33404     expand : function(deep, anim, callback){
33405         if(!this.expanded){
33406             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33407                 return;
33408             }
33409             if(!this.childrenRendered){
33410                 this.renderChildren();
33411             }
33412             this.expanded = true;
33413             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33414                 this.ui.animExpand(function(){
33415                     this.fireEvent("expand", this);
33416                     if(typeof callback == "function"){
33417                         callback(this);
33418                     }
33419                     if(deep === true){
33420                         this.expandChildNodes(true);
33421                     }
33422                 }.createDelegate(this));
33423                 return;
33424             }else{
33425                 this.ui.expand();
33426                 this.fireEvent("expand", this);
33427                 if(typeof callback == "function"){
33428                     callback(this);
33429                 }
33430             }
33431         }else{
33432            if(typeof callback == "function"){
33433                callback(this);
33434            }
33435         }
33436         if(deep === true){
33437             this.expandChildNodes(true);
33438         }
33439     },
33440
33441     isHiddenRoot : function(){
33442         return this.isRoot && !this.getOwnerTree().rootVisible;
33443     },
33444
33445     /**
33446      * Collapse this node.
33447      * @param {Boolean} deep (optional) True to collapse all children as well
33448      * @param {Boolean} anim (optional) false to cancel the default animation
33449      */
33450     collapse : function(deep, anim){
33451         if(this.expanded && !this.isHiddenRoot()){
33452             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33453                 return;
33454             }
33455             this.expanded = false;
33456             if((this.getOwnerTree().animate && anim !== false) || anim){
33457                 this.ui.animCollapse(function(){
33458                     this.fireEvent("collapse", this);
33459                     if(deep === true){
33460                         this.collapseChildNodes(true);
33461                     }
33462                 }.createDelegate(this));
33463                 return;
33464             }else{
33465                 this.ui.collapse();
33466                 this.fireEvent("collapse", this);
33467             }
33468         }
33469         if(deep === true){
33470             var cs = this.childNodes;
33471             for(var i = 0, len = cs.length; i < len; i++) {
33472                 cs[i].collapse(true, false);
33473             }
33474         }
33475     },
33476
33477     // private
33478     delayedExpand : function(delay){
33479         if(!this.expandProcId){
33480             this.expandProcId = this.expand.defer(delay, this);
33481         }
33482     },
33483
33484     // private
33485     cancelExpand : function(){
33486         if(this.expandProcId){
33487             clearTimeout(this.expandProcId);
33488         }
33489         this.expandProcId = false;
33490     },
33491
33492     /**
33493      * Toggles expanded/collapsed state of the node
33494      */
33495     toggle : function(){
33496         if(this.expanded){
33497             this.collapse();
33498         }else{
33499             this.expand();
33500         }
33501     },
33502
33503     /**
33504      * Ensures all parent nodes are expanded
33505      */
33506     ensureVisible : function(callback){
33507         var tree = this.getOwnerTree();
33508         tree.expandPath(this.parentNode.getPath(), false, function(){
33509             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33510             Roo.callback(callback);
33511         }.createDelegate(this));
33512     },
33513
33514     /**
33515      * Expand all child nodes
33516      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33517      */
33518     expandChildNodes : function(deep){
33519         var cs = this.childNodes;
33520         for(var i = 0, len = cs.length; i < len; i++) {
33521                 cs[i].expand(deep);
33522         }
33523     },
33524
33525     /**
33526      * Collapse all child nodes
33527      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33528      */
33529     collapseChildNodes : function(deep){
33530         var cs = this.childNodes;
33531         for(var i = 0, len = cs.length; i < len; i++) {
33532                 cs[i].collapse(deep);
33533         }
33534     },
33535
33536     /**
33537      * Disables this node
33538      */
33539     disable : function(){
33540         this.disabled = true;
33541         this.unselect();
33542         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33543             this.ui.onDisableChange(this, true);
33544         }
33545         this.fireEvent("disabledchange", this, true);
33546     },
33547
33548     /**
33549      * Enables this node
33550      */
33551     enable : function(){
33552         this.disabled = false;
33553         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33554             this.ui.onDisableChange(this, false);
33555         }
33556         this.fireEvent("disabledchange", this, false);
33557     },
33558
33559     // private
33560     renderChildren : function(suppressEvent){
33561         if(suppressEvent !== false){
33562             this.fireEvent("beforechildrenrendered", this);
33563         }
33564         var cs = this.childNodes;
33565         for(var i = 0, len = cs.length; i < len; i++){
33566             cs[i].render(true);
33567         }
33568         this.childrenRendered = true;
33569     },
33570
33571     // private
33572     sort : function(fn, scope){
33573         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33574         if(this.childrenRendered){
33575             var cs = this.childNodes;
33576             for(var i = 0, len = cs.length; i < len; i++){
33577                 cs[i].render(true);
33578             }
33579         }
33580     },
33581
33582     // private
33583     render : function(bulkRender){
33584         this.ui.render(bulkRender);
33585         if(!this.rendered){
33586             this.rendered = true;
33587             if(this.expanded){
33588                 this.expanded = false;
33589                 this.expand(false, false);
33590             }
33591         }
33592     },
33593
33594     // private
33595     renderIndent : function(deep, refresh){
33596         if(refresh){
33597             this.ui.childIndent = null;
33598         }
33599         this.ui.renderIndent();
33600         if(deep === true && this.childrenRendered){
33601             var cs = this.childNodes;
33602             for(var i = 0, len = cs.length; i < len; i++){
33603                 cs[i].renderIndent(true, refresh);
33604             }
33605         }
33606     }
33607 });/*
33608  * Based on:
33609  * Ext JS Library 1.1.1
33610  * Copyright(c) 2006-2007, Ext JS, LLC.
33611  *
33612  * Originally Released Under LGPL - original licence link has changed is not relivant.
33613  *
33614  * Fork - LGPL
33615  * <script type="text/javascript">
33616  */
33617  
33618 /**
33619  * @class Roo.tree.AsyncTreeNode
33620  * @extends Roo.tree.TreeNode
33621  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33622  * @constructor
33623  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33624  */
33625  Roo.tree.AsyncTreeNode = function(config){
33626     this.loaded = false;
33627     this.loading = false;
33628     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33629     /**
33630     * @event beforeload
33631     * Fires before this node is loaded, return false to cancel
33632     * @param {Node} this This node
33633     */
33634     this.addEvents({'beforeload':true, 'load': true});
33635     /**
33636     * @event load
33637     * Fires when this node is loaded
33638     * @param {Node} this This node
33639     */
33640     /**
33641      * The loader used by this node (defaults to using the tree's defined loader)
33642      * @type TreeLoader
33643      * @property loader
33644      */
33645 };
33646 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33647     expand : function(deep, anim, callback){
33648         if(this.loading){ // if an async load is already running, waiting til it's done
33649             var timer;
33650             var f = function(){
33651                 if(!this.loading){ // done loading
33652                     clearInterval(timer);
33653                     this.expand(deep, anim, callback);
33654                 }
33655             }.createDelegate(this);
33656             timer = setInterval(f, 200);
33657             return;
33658         }
33659         if(!this.loaded){
33660             if(this.fireEvent("beforeload", this) === false){
33661                 return;
33662             }
33663             this.loading = true;
33664             this.ui.beforeLoad(this);
33665             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33666             if(loader){
33667                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33668                 return;
33669             }
33670         }
33671         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33672     },
33673     
33674     /**
33675      * Returns true if this node is currently loading
33676      * @return {Boolean}
33677      */
33678     isLoading : function(){
33679         return this.loading;  
33680     },
33681     
33682     loadComplete : function(deep, anim, callback){
33683         this.loading = false;
33684         this.loaded = true;
33685         this.ui.afterLoad(this);
33686         this.fireEvent("load", this);
33687         this.expand(deep, anim, callback);
33688     },
33689     
33690     /**
33691      * Returns true if this node has been loaded
33692      * @return {Boolean}
33693      */
33694     isLoaded : function(){
33695         return this.loaded;
33696     },
33697     
33698     hasChildNodes : function(){
33699         if(!this.isLeaf() && !this.loaded){
33700             return true;
33701         }else{
33702             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33703         }
33704     },
33705
33706     /**
33707      * Trigger a reload for this node
33708      * @param {Function} callback
33709      */
33710     reload : function(callback){
33711         this.collapse(false, false);
33712         while(this.firstChild){
33713             this.removeChild(this.firstChild);
33714         }
33715         this.childrenRendered = false;
33716         this.loaded = false;
33717         if(this.isHiddenRoot()){
33718             this.expanded = false;
33719         }
33720         this.expand(false, false, callback);
33721     }
33722 });/*
33723  * Based on:
33724  * Ext JS Library 1.1.1
33725  * Copyright(c) 2006-2007, Ext JS, LLC.
33726  *
33727  * Originally Released Under LGPL - original licence link has changed is not relivant.
33728  *
33729  * Fork - LGPL
33730  * <script type="text/javascript">
33731  */
33732  
33733 /**
33734  * @class Roo.tree.TreeNodeUI
33735  * @constructor
33736  * @param {Object} node The node to render
33737  * The TreeNode UI implementation is separate from the
33738  * tree implementation. Unless you are customizing the tree UI,
33739  * you should never have to use this directly.
33740  */
33741 Roo.tree.TreeNodeUI = function(node){
33742     this.node = node;
33743     this.rendered = false;
33744     this.animating = false;
33745     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33746 };
33747
33748 Roo.tree.TreeNodeUI.prototype = {
33749     removeChild : function(node){
33750         if(this.rendered){
33751             this.ctNode.removeChild(node.ui.getEl());
33752         }
33753     },
33754
33755     beforeLoad : function(){
33756          this.addClass("x-tree-node-loading");
33757     },
33758
33759     afterLoad : function(){
33760          this.removeClass("x-tree-node-loading");
33761     },
33762
33763     onTextChange : function(node, text, oldText){
33764         if(this.rendered){
33765             this.textNode.innerHTML = text;
33766         }
33767     },
33768
33769     onDisableChange : function(node, state){
33770         this.disabled = state;
33771         if(state){
33772             this.addClass("x-tree-node-disabled");
33773         }else{
33774             this.removeClass("x-tree-node-disabled");
33775         }
33776     },
33777
33778     onSelectedChange : function(state){
33779         if(state){
33780             this.focus();
33781             this.addClass("x-tree-selected");
33782         }else{
33783             //this.blur();
33784             this.removeClass("x-tree-selected");
33785         }
33786     },
33787
33788     onMove : function(tree, node, oldParent, newParent, index, refNode){
33789         this.childIndent = null;
33790         if(this.rendered){
33791             var targetNode = newParent.ui.getContainer();
33792             if(!targetNode){//target not rendered
33793                 this.holder = document.createElement("div");
33794                 this.holder.appendChild(this.wrap);
33795                 return;
33796             }
33797             var insertBefore = refNode ? refNode.ui.getEl() : null;
33798             if(insertBefore){
33799                 targetNode.insertBefore(this.wrap, insertBefore);
33800             }else{
33801                 targetNode.appendChild(this.wrap);
33802             }
33803             this.node.renderIndent(true);
33804         }
33805     },
33806
33807     addClass : function(cls){
33808         if(this.elNode){
33809             Roo.fly(this.elNode).addClass(cls);
33810         }
33811     },
33812
33813     removeClass : function(cls){
33814         if(this.elNode){
33815             Roo.fly(this.elNode).removeClass(cls);
33816         }
33817     },
33818
33819     remove : function(){
33820         if(this.rendered){
33821             this.holder = document.createElement("div");
33822             this.holder.appendChild(this.wrap);
33823         }
33824     },
33825
33826     fireEvent : function(){
33827         return this.node.fireEvent.apply(this.node, arguments);
33828     },
33829
33830     initEvents : function(){
33831         this.node.on("move", this.onMove, this);
33832         var E = Roo.EventManager;
33833         var a = this.anchor;
33834
33835         var el = Roo.fly(a, '_treeui');
33836
33837         if(Roo.isOpera){ // opera render bug ignores the CSS
33838             el.setStyle("text-decoration", "none");
33839         }
33840
33841         el.on("click", this.onClick, this);
33842         el.on("dblclick", this.onDblClick, this);
33843
33844         if(this.checkbox){
33845             Roo.EventManager.on(this.checkbox,
33846                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33847         }
33848
33849         el.on("contextmenu", this.onContextMenu, this);
33850
33851         var icon = Roo.fly(this.iconNode);
33852         icon.on("click", this.onClick, this);
33853         icon.on("dblclick", this.onDblClick, this);
33854         icon.on("contextmenu", this.onContextMenu, this);
33855         E.on(this.ecNode, "click", this.ecClick, this, true);
33856
33857         if(this.node.disabled){
33858             this.addClass("x-tree-node-disabled");
33859         }
33860         if(this.node.hidden){
33861             this.addClass("x-tree-node-disabled");
33862         }
33863         var ot = this.node.getOwnerTree();
33864         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33865         if(dd && (!this.node.isRoot || ot.rootVisible)){
33866             Roo.dd.Registry.register(this.elNode, {
33867                 node: this.node,
33868                 handles: this.getDDHandles(),
33869                 isHandle: false
33870             });
33871         }
33872     },
33873
33874     getDDHandles : function(){
33875         return [this.iconNode, this.textNode];
33876     },
33877
33878     hide : function(){
33879         if(this.rendered){
33880             this.wrap.style.display = "none";
33881         }
33882     },
33883
33884     show : function(){
33885         if(this.rendered){
33886             this.wrap.style.display = "";
33887         }
33888     },
33889
33890     onContextMenu : function(e){
33891         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33892             e.preventDefault();
33893             this.focus();
33894             this.fireEvent("contextmenu", this.node, e);
33895         }
33896     },
33897
33898     onClick : function(e){
33899         if(this.dropping){
33900             e.stopEvent();
33901             return;
33902         }
33903         if(this.fireEvent("beforeclick", this.node, e) !== false){
33904             if(!this.disabled && this.node.attributes.href){
33905                 this.fireEvent("click", this.node, e);
33906                 return;
33907             }
33908             e.preventDefault();
33909             if(this.disabled){
33910                 return;
33911             }
33912
33913             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33914                 this.node.toggle();
33915             }
33916
33917             this.fireEvent("click", this.node, e);
33918         }else{
33919             e.stopEvent();
33920         }
33921     },
33922
33923     onDblClick : function(e){
33924         e.preventDefault();
33925         if(this.disabled){
33926             return;
33927         }
33928         if(this.checkbox){
33929             this.toggleCheck();
33930         }
33931         if(!this.animating && this.node.hasChildNodes()){
33932             this.node.toggle();
33933         }
33934         this.fireEvent("dblclick", this.node, e);
33935     },
33936
33937     onCheckChange : function(){
33938         var checked = this.checkbox.checked;
33939         this.node.attributes.checked = checked;
33940         this.fireEvent('checkchange', this.node, checked);
33941     },
33942
33943     ecClick : function(e){
33944         if(!this.animating && this.node.hasChildNodes()){
33945             this.node.toggle();
33946         }
33947     },
33948
33949     startDrop : function(){
33950         this.dropping = true;
33951     },
33952
33953     // delayed drop so the click event doesn't get fired on a drop
33954     endDrop : function(){
33955        setTimeout(function(){
33956            this.dropping = false;
33957        }.createDelegate(this), 50);
33958     },
33959
33960     expand : function(){
33961         this.updateExpandIcon();
33962         this.ctNode.style.display = "";
33963     },
33964
33965     focus : function(){
33966         if(!this.node.preventHScroll){
33967             try{this.anchor.focus();
33968             }catch(e){}
33969         }else if(!Roo.isIE){
33970             try{
33971                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33972                 var l = noscroll.scrollLeft;
33973                 this.anchor.focus();
33974                 noscroll.scrollLeft = l;
33975             }catch(e){}
33976         }
33977     },
33978
33979     toggleCheck : function(value){
33980         var cb = this.checkbox;
33981         if(cb){
33982             cb.checked = (value === undefined ? !cb.checked : value);
33983         }
33984     },
33985
33986     blur : function(){
33987         try{
33988             this.anchor.blur();
33989         }catch(e){}
33990     },
33991
33992     animExpand : function(callback){
33993         var ct = Roo.get(this.ctNode);
33994         ct.stopFx();
33995         if(!this.node.hasChildNodes()){
33996             this.updateExpandIcon();
33997             this.ctNode.style.display = "";
33998             Roo.callback(callback);
33999             return;
34000         }
34001         this.animating = true;
34002         this.updateExpandIcon();
34003
34004         ct.slideIn('t', {
34005            callback : function(){
34006                this.animating = false;
34007                Roo.callback(callback);
34008             },
34009             scope: this,
34010             duration: this.node.ownerTree.duration || .25
34011         });
34012     },
34013
34014     highlight : function(){
34015         var tree = this.node.getOwnerTree();
34016         Roo.fly(this.wrap).highlight(
34017             tree.hlColor || "C3DAF9",
34018             {endColor: tree.hlBaseColor}
34019         );
34020     },
34021
34022     collapse : function(){
34023         this.updateExpandIcon();
34024         this.ctNode.style.display = "none";
34025     },
34026
34027     animCollapse : function(callback){
34028         var ct = Roo.get(this.ctNode);
34029         ct.enableDisplayMode('block');
34030         ct.stopFx();
34031
34032         this.animating = true;
34033         this.updateExpandIcon();
34034
34035         ct.slideOut('t', {
34036             callback : function(){
34037                this.animating = false;
34038                Roo.callback(callback);
34039             },
34040             scope: this,
34041             duration: this.node.ownerTree.duration || .25
34042         });
34043     },
34044
34045     getContainer : function(){
34046         return this.ctNode;
34047     },
34048
34049     getEl : function(){
34050         return this.wrap;
34051     },
34052
34053     appendDDGhost : function(ghostNode){
34054         ghostNode.appendChild(this.elNode.cloneNode(true));
34055     },
34056
34057     getDDRepairXY : function(){
34058         return Roo.lib.Dom.getXY(this.iconNode);
34059     },
34060
34061     onRender : function(){
34062         this.render();
34063     },
34064
34065     render : function(bulkRender){
34066         var n = this.node, a = n.attributes;
34067         var targetNode = n.parentNode ?
34068               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34069
34070         if(!this.rendered){
34071             this.rendered = true;
34072
34073             this.renderElements(n, a, targetNode, bulkRender);
34074
34075             if(a.qtip){
34076                if(this.textNode.setAttributeNS){
34077                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34078                    if(a.qtipTitle){
34079                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34080                    }
34081                }else{
34082                    this.textNode.setAttribute("ext:qtip", a.qtip);
34083                    if(a.qtipTitle){
34084                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34085                    }
34086                }
34087             }else if(a.qtipCfg){
34088                 a.qtipCfg.target = Roo.id(this.textNode);
34089                 Roo.QuickTips.register(a.qtipCfg);
34090             }
34091             this.initEvents();
34092             if(!this.node.expanded){
34093                 this.updateExpandIcon();
34094             }
34095         }else{
34096             if(bulkRender === true) {
34097                 targetNode.appendChild(this.wrap);
34098             }
34099         }
34100     },
34101
34102     renderElements : function(n, a, targetNode, bulkRender)
34103     {
34104         // add some indent caching, this helps performance when rendering a large tree
34105         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34106         var t = n.getOwnerTree();
34107         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34108         if (typeof(n.attributes.html) != 'undefined') {
34109             txt = n.attributes.html;
34110         }
34111         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34112         var cb = typeof a.checked == 'boolean';
34113         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34114         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34115             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34116             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34117             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34118             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34119             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34120              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34121                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34122             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34123             "</li>"];
34124
34125         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34126             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34127                                 n.nextSibling.ui.getEl(), buf.join(""));
34128         }else{
34129             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34130         }
34131
34132         this.elNode = this.wrap.childNodes[0];
34133         this.ctNode = this.wrap.childNodes[1];
34134         var cs = this.elNode.childNodes;
34135         this.indentNode = cs[0];
34136         this.ecNode = cs[1];
34137         this.iconNode = cs[2];
34138         var index = 3;
34139         if(cb){
34140             this.checkbox = cs[3];
34141             index++;
34142         }
34143         this.anchor = cs[index];
34144         this.textNode = cs[index].firstChild;
34145     },
34146
34147     getAnchor : function(){
34148         return this.anchor;
34149     },
34150
34151     getTextEl : function(){
34152         return this.textNode;
34153     },
34154
34155     getIconEl : function(){
34156         return this.iconNode;
34157     },
34158
34159     isChecked : function(){
34160         return this.checkbox ? this.checkbox.checked : false;
34161     },
34162
34163     updateExpandIcon : function(){
34164         if(this.rendered){
34165             var n = this.node, c1, c2;
34166             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34167             var hasChild = n.hasChildNodes();
34168             if(hasChild){
34169                 if(n.expanded){
34170                     cls += "-minus";
34171                     c1 = "x-tree-node-collapsed";
34172                     c2 = "x-tree-node-expanded";
34173                 }else{
34174                     cls += "-plus";
34175                     c1 = "x-tree-node-expanded";
34176                     c2 = "x-tree-node-collapsed";
34177                 }
34178                 if(this.wasLeaf){
34179                     this.removeClass("x-tree-node-leaf");
34180                     this.wasLeaf = false;
34181                 }
34182                 if(this.c1 != c1 || this.c2 != c2){
34183                     Roo.fly(this.elNode).replaceClass(c1, c2);
34184                     this.c1 = c1; this.c2 = c2;
34185                 }
34186             }else{
34187                 // this changes non-leafs into leafs if they have no children.
34188                 // it's not very rational behaviour..
34189                 
34190                 if(!this.wasLeaf && this.node.leaf){
34191                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34192                     delete this.c1;
34193                     delete this.c2;
34194                     this.wasLeaf = true;
34195                 }
34196             }
34197             var ecc = "x-tree-ec-icon "+cls;
34198             if(this.ecc != ecc){
34199                 this.ecNode.className = ecc;
34200                 this.ecc = ecc;
34201             }
34202         }
34203     },
34204
34205     getChildIndent : function(){
34206         if(!this.childIndent){
34207             var buf = [];
34208             var p = this.node;
34209             while(p){
34210                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34211                     if(!p.isLast()) {
34212                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34213                     } else {
34214                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34215                     }
34216                 }
34217                 p = p.parentNode;
34218             }
34219             this.childIndent = buf.join("");
34220         }
34221         return this.childIndent;
34222     },
34223
34224     renderIndent : function(){
34225         if(this.rendered){
34226             var indent = "";
34227             var p = this.node.parentNode;
34228             if(p){
34229                 indent = p.ui.getChildIndent();
34230             }
34231             if(this.indentMarkup != indent){ // don't rerender if not required
34232                 this.indentNode.innerHTML = indent;
34233                 this.indentMarkup = indent;
34234             }
34235             this.updateExpandIcon();
34236         }
34237     }
34238 };
34239
34240 Roo.tree.RootTreeNodeUI = function(){
34241     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34242 };
34243 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34244     render : function(){
34245         if(!this.rendered){
34246             var targetNode = this.node.ownerTree.innerCt.dom;
34247             this.node.expanded = true;
34248             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34249             this.wrap = this.ctNode = targetNode.firstChild;
34250         }
34251     },
34252     collapse : function(){
34253     },
34254     expand : function(){
34255     }
34256 });/*
34257  * Based on:
34258  * Ext JS Library 1.1.1
34259  * Copyright(c) 2006-2007, Ext JS, LLC.
34260  *
34261  * Originally Released Under LGPL - original licence link has changed is not relivant.
34262  *
34263  * Fork - LGPL
34264  * <script type="text/javascript">
34265  */
34266 /**
34267  * @class Roo.tree.TreeLoader
34268  * @extends Roo.util.Observable
34269  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34270  * nodes from a specified URL. The response must be a javascript Array definition
34271  * who's elements are node definition objects. eg:
34272  * <pre><code>
34273 {  success : true,
34274    data :      [
34275    
34276     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34277     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34278     ]
34279 }
34280
34281
34282 </code></pre>
34283  * <br><br>
34284  * The old style respose with just an array is still supported, but not recommended.
34285  * <br><br>
34286  *
34287  * A server request is sent, and child nodes are loaded only when a node is expanded.
34288  * The loading node's id is passed to the server under the parameter name "node" to
34289  * enable the server to produce the correct child nodes.
34290  * <br><br>
34291  * To pass extra parameters, an event handler may be attached to the "beforeload"
34292  * event, and the parameters specified in the TreeLoader's baseParams property:
34293  * <pre><code>
34294     myTreeLoader.on("beforeload", function(treeLoader, node) {
34295         this.baseParams.category = node.attributes.category;
34296     }, this);
34297 </code></pre><
34298  * This would pass an HTTP parameter called "category" to the server containing
34299  * the value of the Node's "category" attribute.
34300  * @constructor
34301  * Creates a new Treeloader.
34302  * @param {Object} config A config object containing config properties.
34303  */
34304 Roo.tree.TreeLoader = function(config){
34305     this.baseParams = {};
34306     this.requestMethod = "POST";
34307     Roo.apply(this, config);
34308
34309     this.addEvents({
34310     
34311         /**
34312          * @event beforeload
34313          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34314          * @param {Object} This TreeLoader object.
34315          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34316          * @param {Object} callback The callback function specified in the {@link #load} call.
34317          */
34318         beforeload : true,
34319         /**
34320          * @event load
34321          * Fires when the node has been successfuly loaded.
34322          * @param {Object} This TreeLoader object.
34323          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34324          * @param {Object} response The response object containing the data from the server.
34325          */
34326         load : true,
34327         /**
34328          * @event loadexception
34329          * Fires if the network request failed.
34330          * @param {Object} This TreeLoader object.
34331          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34332          * @param {Object} response The response object containing the data from the server.
34333          */
34334         loadexception : true,
34335         /**
34336          * @event create
34337          * Fires before a node is created, enabling you to return custom Node types 
34338          * @param {Object} This TreeLoader object.
34339          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34340          */
34341         create : true
34342     });
34343
34344     Roo.tree.TreeLoader.superclass.constructor.call(this);
34345 };
34346
34347 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34348     /**
34349     * @cfg {String} dataUrl The URL from which to request a Json string which
34350     * specifies an array of node definition object representing the child nodes
34351     * to be loaded.
34352     */
34353     /**
34354     * @cfg {String} requestMethod either GET or POST
34355     * defaults to POST (due to BC)
34356     * to be loaded.
34357     */
34358     /**
34359     * @cfg {Object} baseParams (optional) An object containing properties which
34360     * specify HTTP parameters to be passed to each request for child nodes.
34361     */
34362     /**
34363     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34364     * created by this loader. If the attributes sent by the server have an attribute in this object,
34365     * they take priority.
34366     */
34367     /**
34368     * @cfg {Object} uiProviders (optional) An object containing properties which
34369     * 
34370     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34371     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34372     * <i>uiProvider</i> attribute of a returned child node is a string rather
34373     * than a reference to a TreeNodeUI implementation, this that string value
34374     * is used as a property name in the uiProviders object. You can define the provider named
34375     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34376     */
34377     uiProviders : {},
34378
34379     /**
34380     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34381     * child nodes before loading.
34382     */
34383     clearOnLoad : true,
34384
34385     /**
34386     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34387     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34388     * Grid query { data : [ .....] }
34389     */
34390     
34391     root : false,
34392      /**
34393     * @cfg {String} queryParam (optional) 
34394     * Name of the query as it will be passed on the querystring (defaults to 'node')
34395     * eg. the request will be ?node=[id]
34396     */
34397     
34398     
34399     queryParam: false,
34400     
34401     /**
34402      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34403      * This is called automatically when a node is expanded, but may be used to reload
34404      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34405      * @param {Roo.tree.TreeNode} node
34406      * @param {Function} callback
34407      */
34408     load : function(node, callback){
34409         if(this.clearOnLoad){
34410             while(node.firstChild){
34411                 node.removeChild(node.firstChild);
34412             }
34413         }
34414         if(node.attributes.children){ // preloaded json children
34415             var cs = node.attributes.children;
34416             for(var i = 0, len = cs.length; i < len; i++){
34417                 node.appendChild(this.createNode(cs[i]));
34418             }
34419             if(typeof callback == "function"){
34420                 callback();
34421             }
34422         }else if(this.dataUrl){
34423             this.requestData(node, callback);
34424         }
34425     },
34426
34427     getParams: function(node){
34428         var buf = [], bp = this.baseParams;
34429         for(var key in bp){
34430             if(typeof bp[key] != "function"){
34431                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34432             }
34433         }
34434         var n = this.queryParam === false ? 'node' : this.queryParam;
34435         buf.push(n + "=", encodeURIComponent(node.id));
34436         return buf.join("");
34437     },
34438
34439     requestData : function(node, callback){
34440         if(this.fireEvent("beforeload", this, node, callback) !== false){
34441             this.transId = Roo.Ajax.request({
34442                 method:this.requestMethod,
34443                 url: this.dataUrl||this.url,
34444                 success: this.handleResponse,
34445                 failure: this.handleFailure,
34446                 scope: this,
34447                 argument: {callback: callback, node: node},
34448                 params: this.getParams(node)
34449             });
34450         }else{
34451             // if the load is cancelled, make sure we notify
34452             // the node that we are done
34453             if(typeof callback == "function"){
34454                 callback();
34455             }
34456         }
34457     },
34458
34459     isLoading : function(){
34460         return this.transId ? true : false;
34461     },
34462
34463     abort : function(){
34464         if(this.isLoading()){
34465             Roo.Ajax.abort(this.transId);
34466         }
34467     },
34468
34469     // private
34470     createNode : function(attr)
34471     {
34472         // apply baseAttrs, nice idea Corey!
34473         if(this.baseAttrs){
34474             Roo.applyIf(attr, this.baseAttrs);
34475         }
34476         if(this.applyLoader !== false){
34477             attr.loader = this;
34478         }
34479         // uiProvider = depreciated..
34480         
34481         if(typeof(attr.uiProvider) == 'string'){
34482            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34483                 /**  eval:var:attr */ eval(attr.uiProvider);
34484         }
34485         if(typeof(this.uiProviders['default']) != 'undefined') {
34486             attr.uiProvider = this.uiProviders['default'];
34487         }
34488         
34489         this.fireEvent('create', this, attr);
34490         
34491         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34492         return(attr.leaf ?
34493                         new Roo.tree.TreeNode(attr) :
34494                         new Roo.tree.AsyncTreeNode(attr));
34495     },
34496
34497     processResponse : function(response, node, callback)
34498     {
34499         var json = response.responseText;
34500         try {
34501             
34502             var o = Roo.decode(json);
34503             
34504             if (this.root === false && typeof(o.success) != undefined) {
34505                 this.root = 'data'; // the default behaviour for list like data..
34506                 }
34507                 
34508             if (this.root !== false &&  !o.success) {
34509                 // it's a failure condition.
34510                 var a = response.argument;
34511                 this.fireEvent("loadexception", this, a.node, response);
34512                 Roo.log("Load failed - should have a handler really");
34513                 return;
34514             }
34515             
34516             
34517             
34518             if (this.root !== false) {
34519                  o = o[this.root];
34520             }
34521             
34522             for(var i = 0, len = o.length; i < len; i++){
34523                 var n = this.createNode(o[i]);
34524                 if(n){
34525                     node.appendChild(n);
34526                 }
34527             }
34528             if(typeof callback == "function"){
34529                 callback(this, node);
34530             }
34531         }catch(e){
34532             this.handleFailure(response);
34533         }
34534     },
34535
34536     handleResponse : function(response){
34537         this.transId = false;
34538         var a = response.argument;
34539         this.processResponse(response, a.node, a.callback);
34540         this.fireEvent("load", this, a.node, response);
34541     },
34542
34543     handleFailure : function(response)
34544     {
34545         // should handle failure better..
34546         this.transId = false;
34547         var a = response.argument;
34548         this.fireEvent("loadexception", this, a.node, response);
34549         if(typeof a.callback == "function"){
34550             a.callback(this, a.node);
34551         }
34552     }
34553 });/*
34554  * Based on:
34555  * Ext JS Library 1.1.1
34556  * Copyright(c) 2006-2007, Ext JS, LLC.
34557  *
34558  * Originally Released Under LGPL - original licence link has changed is not relivant.
34559  *
34560  * Fork - LGPL
34561  * <script type="text/javascript">
34562  */
34563
34564 /**
34565 * @class Roo.tree.TreeFilter
34566 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34567 * @param {TreePanel} tree
34568 * @param {Object} config (optional)
34569  */
34570 Roo.tree.TreeFilter = function(tree, config){
34571     this.tree = tree;
34572     this.filtered = {};
34573     Roo.apply(this, config);
34574 };
34575
34576 Roo.tree.TreeFilter.prototype = {
34577     clearBlank:false,
34578     reverse:false,
34579     autoClear:false,
34580     remove:false,
34581
34582      /**
34583      * Filter the data by a specific attribute.
34584      * @param {String/RegExp} value Either string that the attribute value
34585      * should start with or a RegExp to test against the attribute
34586      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34587      * @param {TreeNode} startNode (optional) The node to start the filter at.
34588      */
34589     filter : function(value, attr, startNode){
34590         attr = attr || "text";
34591         var f;
34592         if(typeof value == "string"){
34593             var vlen = value.length;
34594             // auto clear empty filter
34595             if(vlen == 0 && this.clearBlank){
34596                 this.clear();
34597                 return;
34598             }
34599             value = value.toLowerCase();
34600             f = function(n){
34601                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34602             };
34603         }else if(value.exec){ // regex?
34604             f = function(n){
34605                 return value.test(n.attributes[attr]);
34606             };
34607         }else{
34608             throw 'Illegal filter type, must be string or regex';
34609         }
34610         this.filterBy(f, null, startNode);
34611         },
34612
34613     /**
34614      * Filter by a function. The passed function will be called with each
34615      * node in the tree (or from the startNode). If the function returns true, the node is kept
34616      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34617      * @param {Function} fn The filter function
34618      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34619      */
34620     filterBy : function(fn, scope, startNode){
34621         startNode = startNode || this.tree.root;
34622         if(this.autoClear){
34623             this.clear();
34624         }
34625         var af = this.filtered, rv = this.reverse;
34626         var f = function(n){
34627             if(n == startNode){
34628                 return true;
34629             }
34630             if(af[n.id]){
34631                 return false;
34632             }
34633             var m = fn.call(scope || n, n);
34634             if(!m || rv){
34635                 af[n.id] = n;
34636                 n.ui.hide();
34637                 return false;
34638             }
34639             return true;
34640         };
34641         startNode.cascade(f);
34642         if(this.remove){
34643            for(var id in af){
34644                if(typeof id != "function"){
34645                    var n = af[id];
34646                    if(n && n.parentNode){
34647                        n.parentNode.removeChild(n);
34648                    }
34649                }
34650            }
34651         }
34652     },
34653
34654     /**
34655      * Clears the current filter. Note: with the "remove" option
34656      * set a filter cannot be cleared.
34657      */
34658     clear : function(){
34659         var t = this.tree;
34660         var af = this.filtered;
34661         for(var id in af){
34662             if(typeof id != "function"){
34663                 var n = af[id];
34664                 if(n){
34665                     n.ui.show();
34666                 }
34667             }
34668         }
34669         this.filtered = {};
34670     }
34671 };
34672 /*
34673  * Based on:
34674  * Ext JS Library 1.1.1
34675  * Copyright(c) 2006-2007, Ext JS, LLC.
34676  *
34677  * Originally Released Under LGPL - original licence link has changed is not relivant.
34678  *
34679  * Fork - LGPL
34680  * <script type="text/javascript">
34681  */
34682  
34683
34684 /**
34685  * @class Roo.tree.TreeSorter
34686  * Provides sorting of nodes in a TreePanel
34687  * 
34688  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34689  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34690  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34691  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34692  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34693  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34694  * @constructor
34695  * @param {TreePanel} tree
34696  * @param {Object} config
34697  */
34698 Roo.tree.TreeSorter = function(tree, config){
34699     Roo.apply(this, config);
34700     tree.on("beforechildrenrendered", this.doSort, this);
34701     tree.on("append", this.updateSort, this);
34702     tree.on("insert", this.updateSort, this);
34703     
34704     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34705     var p = this.property || "text";
34706     var sortType = this.sortType;
34707     var fs = this.folderSort;
34708     var cs = this.caseSensitive === true;
34709     var leafAttr = this.leafAttr || 'leaf';
34710
34711     this.sortFn = function(n1, n2){
34712         if(fs){
34713             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34714                 return 1;
34715             }
34716             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34717                 return -1;
34718             }
34719         }
34720         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34721         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34722         if(v1 < v2){
34723                         return dsc ? +1 : -1;
34724                 }else if(v1 > v2){
34725                         return dsc ? -1 : +1;
34726         }else{
34727                 return 0;
34728         }
34729     };
34730 };
34731
34732 Roo.tree.TreeSorter.prototype = {
34733     doSort : function(node){
34734         node.sort(this.sortFn);
34735     },
34736     
34737     compareNodes : function(n1, n2){
34738         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34739     },
34740     
34741     updateSort : function(tree, node){
34742         if(node.childrenRendered){
34743             this.doSort.defer(1, this, [node]);
34744         }
34745     }
34746 };/*
34747  * Based on:
34748  * Ext JS Library 1.1.1
34749  * Copyright(c) 2006-2007, Ext JS, LLC.
34750  *
34751  * Originally Released Under LGPL - original licence link has changed is not relivant.
34752  *
34753  * Fork - LGPL
34754  * <script type="text/javascript">
34755  */
34756
34757 if(Roo.dd.DropZone){
34758     
34759 Roo.tree.TreeDropZone = function(tree, config){
34760     this.allowParentInsert = false;
34761     this.allowContainerDrop = false;
34762     this.appendOnly = false;
34763     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34764     this.tree = tree;
34765     this.lastInsertClass = "x-tree-no-status";
34766     this.dragOverData = {};
34767 };
34768
34769 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34770     ddGroup : "TreeDD",
34771     scroll:  true,
34772     
34773     expandDelay : 1000,
34774     
34775     expandNode : function(node){
34776         if(node.hasChildNodes() && !node.isExpanded()){
34777             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34778         }
34779     },
34780     
34781     queueExpand : function(node){
34782         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34783     },
34784     
34785     cancelExpand : function(){
34786         if(this.expandProcId){
34787             clearTimeout(this.expandProcId);
34788             this.expandProcId = false;
34789         }
34790     },
34791     
34792     isValidDropPoint : function(n, pt, dd, e, data){
34793         if(!n || !data){ return false; }
34794         var targetNode = n.node;
34795         var dropNode = data.node;
34796         // default drop rules
34797         if(!(targetNode && targetNode.isTarget && pt)){
34798             return false;
34799         }
34800         if(pt == "append" && targetNode.allowChildren === false){
34801             return false;
34802         }
34803         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34804             return false;
34805         }
34806         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34807             return false;
34808         }
34809         // reuse the object
34810         var overEvent = this.dragOverData;
34811         overEvent.tree = this.tree;
34812         overEvent.target = targetNode;
34813         overEvent.data = data;
34814         overEvent.point = pt;
34815         overEvent.source = dd;
34816         overEvent.rawEvent = e;
34817         overEvent.dropNode = dropNode;
34818         overEvent.cancel = false;  
34819         var result = this.tree.fireEvent("nodedragover", overEvent);
34820         return overEvent.cancel === false && result !== false;
34821     },
34822     
34823     getDropPoint : function(e, n, dd)
34824     {
34825         var tn = n.node;
34826         if(tn.isRoot){
34827             return tn.allowChildren !== false ? "append" : false; // always append for root
34828         }
34829         var dragEl = n.ddel;
34830         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34831         var y = Roo.lib.Event.getPageY(e);
34832         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34833         
34834         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34835         var noAppend = tn.allowChildren === false;
34836         if(this.appendOnly || tn.parentNode.allowChildren === false){
34837             return noAppend ? false : "append";
34838         }
34839         var noBelow = false;
34840         if(!this.allowParentInsert){
34841             noBelow = tn.hasChildNodes() && tn.isExpanded();
34842         }
34843         var q = (b - t) / (noAppend ? 2 : 3);
34844         if(y >= t && y < (t + q)){
34845             return "above";
34846         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34847             return "below";
34848         }else{
34849             return "append";
34850         }
34851     },
34852     
34853     onNodeEnter : function(n, dd, e, data)
34854     {
34855         this.cancelExpand();
34856     },
34857     
34858     onNodeOver : function(n, dd, e, data)
34859     {
34860        
34861         var pt = this.getDropPoint(e, n, dd);
34862         var node = n.node;
34863         
34864         // auto node expand check
34865         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34866             this.queueExpand(node);
34867         }else if(pt != "append"){
34868             this.cancelExpand();
34869         }
34870         
34871         // set the insert point style on the target node
34872         var returnCls = this.dropNotAllowed;
34873         if(this.isValidDropPoint(n, pt, dd, e, data)){
34874            if(pt){
34875                var el = n.ddel;
34876                var cls;
34877                if(pt == "above"){
34878                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34879                    cls = "x-tree-drag-insert-above";
34880                }else if(pt == "below"){
34881                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34882                    cls = "x-tree-drag-insert-below";
34883                }else{
34884                    returnCls = "x-tree-drop-ok-append";
34885                    cls = "x-tree-drag-append";
34886                }
34887                if(this.lastInsertClass != cls){
34888                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34889                    this.lastInsertClass = cls;
34890                }
34891            }
34892        }
34893        return returnCls;
34894     },
34895     
34896     onNodeOut : function(n, dd, e, data){
34897         
34898         this.cancelExpand();
34899         this.removeDropIndicators(n);
34900     },
34901     
34902     onNodeDrop : function(n, dd, e, data){
34903         var point = this.getDropPoint(e, n, dd);
34904         var targetNode = n.node;
34905         targetNode.ui.startDrop();
34906         if(!this.isValidDropPoint(n, point, dd, e, data)){
34907             targetNode.ui.endDrop();
34908             return false;
34909         }
34910         // first try to find the drop node
34911         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34912         var dropEvent = {
34913             tree : this.tree,
34914             target: targetNode,
34915             data: data,
34916             point: point,
34917             source: dd,
34918             rawEvent: e,
34919             dropNode: dropNode,
34920             cancel: !dropNode   
34921         };
34922         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34923         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34924             targetNode.ui.endDrop();
34925             return false;
34926         }
34927         // allow target changing
34928         targetNode = dropEvent.target;
34929         if(point == "append" && !targetNode.isExpanded()){
34930             targetNode.expand(false, null, function(){
34931                 this.completeDrop(dropEvent);
34932             }.createDelegate(this));
34933         }else{
34934             this.completeDrop(dropEvent);
34935         }
34936         return true;
34937     },
34938     
34939     completeDrop : function(de){
34940         var ns = de.dropNode, p = de.point, t = de.target;
34941         if(!(ns instanceof Array)){
34942             ns = [ns];
34943         }
34944         var n;
34945         for(var i = 0, len = ns.length; i < len; i++){
34946             n = ns[i];
34947             if(p == "above"){
34948                 t.parentNode.insertBefore(n, t);
34949             }else if(p == "below"){
34950                 t.parentNode.insertBefore(n, t.nextSibling);
34951             }else{
34952                 t.appendChild(n);
34953             }
34954         }
34955         n.ui.focus();
34956         if(this.tree.hlDrop){
34957             n.ui.highlight();
34958         }
34959         t.ui.endDrop();
34960         this.tree.fireEvent("nodedrop", de);
34961     },
34962     
34963     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34964         if(this.tree.hlDrop){
34965             dropNode.ui.focus();
34966             dropNode.ui.highlight();
34967         }
34968         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34969     },
34970     
34971     getTree : function(){
34972         return this.tree;
34973     },
34974     
34975     removeDropIndicators : function(n){
34976         if(n && n.ddel){
34977             var el = n.ddel;
34978             Roo.fly(el).removeClass([
34979                     "x-tree-drag-insert-above",
34980                     "x-tree-drag-insert-below",
34981                     "x-tree-drag-append"]);
34982             this.lastInsertClass = "_noclass";
34983         }
34984     },
34985     
34986     beforeDragDrop : function(target, e, id){
34987         this.cancelExpand();
34988         return true;
34989     },
34990     
34991     afterRepair : function(data){
34992         if(data && Roo.enableFx){
34993             data.node.ui.highlight();
34994         }
34995         this.hideProxy();
34996     } 
34997     
34998 });
34999
35000 }
35001 /*
35002  * Based on:
35003  * Ext JS Library 1.1.1
35004  * Copyright(c) 2006-2007, Ext JS, LLC.
35005  *
35006  * Originally Released Under LGPL - original licence link has changed is not relivant.
35007  *
35008  * Fork - LGPL
35009  * <script type="text/javascript">
35010  */
35011  
35012
35013 if(Roo.dd.DragZone){
35014 Roo.tree.TreeDragZone = function(tree, config){
35015     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35016     this.tree = tree;
35017 };
35018
35019 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35020     ddGroup : "TreeDD",
35021    
35022     onBeforeDrag : function(data, e){
35023         var n = data.node;
35024         return n && n.draggable && !n.disabled;
35025     },
35026      
35027     
35028     onInitDrag : function(e){
35029         var data = this.dragData;
35030         this.tree.getSelectionModel().select(data.node);
35031         this.proxy.update("");
35032         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35033         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35034     },
35035     
35036     getRepairXY : function(e, data){
35037         return data.node.ui.getDDRepairXY();
35038     },
35039     
35040     onEndDrag : function(data, e){
35041         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35042         
35043         
35044     },
35045     
35046     onValidDrop : function(dd, e, id){
35047         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35048         this.hideProxy();
35049     },
35050     
35051     beforeInvalidDrop : function(e, id){
35052         // this scrolls the original position back into view
35053         var sm = this.tree.getSelectionModel();
35054         sm.clearSelections();
35055         sm.select(this.dragData.node);
35056     }
35057 });
35058 }/*
35059  * Based on:
35060  * Ext JS Library 1.1.1
35061  * Copyright(c) 2006-2007, Ext JS, LLC.
35062  *
35063  * Originally Released Under LGPL - original licence link has changed is not relivant.
35064  *
35065  * Fork - LGPL
35066  * <script type="text/javascript">
35067  */
35068 /**
35069  * @class Roo.tree.TreeEditor
35070  * @extends Roo.Editor
35071  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35072  * as the editor field.
35073  * @constructor
35074  * @param {Object} config (used to be the tree panel.)
35075  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35076  * 
35077  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35078  * @cfg {Roo.form.TextField|Object} field The field configuration
35079  *
35080  * 
35081  */
35082 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35083     var tree = config;
35084     var field;
35085     if (oldconfig) { // old style..
35086         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35087     } else {
35088         // new style..
35089         tree = config.tree;
35090         config.field = config.field  || {};
35091         config.field.xtype = 'TextField';
35092         field = Roo.factory(config.field, Roo.form);
35093     }
35094     config = config || {};
35095     
35096     
35097     this.addEvents({
35098         /**
35099          * @event beforenodeedit
35100          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35101          * false from the handler of this event.
35102          * @param {Editor} this
35103          * @param {Roo.tree.Node} node 
35104          */
35105         "beforenodeedit" : true
35106     });
35107     
35108     //Roo.log(config);
35109     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35110
35111     this.tree = tree;
35112
35113     tree.on('beforeclick', this.beforeNodeClick, this);
35114     tree.getTreeEl().on('mousedown', this.hide, this);
35115     this.on('complete', this.updateNode, this);
35116     this.on('beforestartedit', this.fitToTree, this);
35117     this.on('startedit', this.bindScroll, this, {delay:10});
35118     this.on('specialkey', this.onSpecialKey, this);
35119 };
35120
35121 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35122     /**
35123      * @cfg {String} alignment
35124      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35125      */
35126     alignment: "l-l",
35127     // inherit
35128     autoSize: false,
35129     /**
35130      * @cfg {Boolean} hideEl
35131      * True to hide the bound element while the editor is displayed (defaults to false)
35132      */
35133     hideEl : false,
35134     /**
35135      * @cfg {String} cls
35136      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35137      */
35138     cls: "x-small-editor x-tree-editor",
35139     /**
35140      * @cfg {Boolean} shim
35141      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35142      */
35143     shim:false,
35144     // inherit
35145     shadow:"frame",
35146     /**
35147      * @cfg {Number} maxWidth
35148      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35149      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35150      * scroll and client offsets into account prior to each edit.
35151      */
35152     maxWidth: 250,
35153
35154     editDelay : 350,
35155
35156     // private
35157     fitToTree : function(ed, el){
35158         var td = this.tree.getTreeEl().dom, nd = el.dom;
35159         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35160             td.scrollLeft = nd.offsetLeft;
35161         }
35162         var w = Math.min(
35163                 this.maxWidth,
35164                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35165         this.setSize(w, '');
35166         
35167         return this.fireEvent('beforenodeedit', this, this.editNode);
35168         
35169     },
35170
35171     // private
35172     triggerEdit : function(node){
35173         this.completeEdit();
35174         this.editNode = node;
35175         this.startEdit(node.ui.textNode, node.text);
35176     },
35177
35178     // private
35179     bindScroll : function(){
35180         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35181     },
35182
35183     // private
35184     beforeNodeClick : function(node, e){
35185         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35186         this.lastClick = new Date();
35187         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35188             e.stopEvent();
35189             this.triggerEdit(node);
35190             return false;
35191         }
35192         return true;
35193     },
35194
35195     // private
35196     updateNode : function(ed, value){
35197         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35198         this.editNode.setText(value);
35199     },
35200
35201     // private
35202     onHide : function(){
35203         Roo.tree.TreeEditor.superclass.onHide.call(this);
35204         if(this.editNode){
35205             this.editNode.ui.focus();
35206         }
35207     },
35208
35209     // private
35210     onSpecialKey : function(field, e){
35211         var k = e.getKey();
35212         if(k == e.ESC){
35213             e.stopEvent();
35214             this.cancelEdit();
35215         }else if(k == e.ENTER && !e.hasModifier()){
35216             e.stopEvent();
35217             this.completeEdit();
35218         }
35219     }
35220 });//<Script type="text/javascript">
35221 /*
35222  * Based on:
35223  * Ext JS Library 1.1.1
35224  * Copyright(c) 2006-2007, Ext JS, LLC.
35225  *
35226  * Originally Released Under LGPL - original licence link has changed is not relivant.
35227  *
35228  * Fork - LGPL
35229  * <script type="text/javascript">
35230  */
35231  
35232 /**
35233  * Not documented??? - probably should be...
35234  */
35235
35236 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35237     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35238     
35239     renderElements : function(n, a, targetNode, bulkRender){
35240         //consel.log("renderElements?");
35241         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35242
35243         var t = n.getOwnerTree();
35244         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35245         
35246         var cols = t.columns;
35247         var bw = t.borderWidth;
35248         var c = cols[0];
35249         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35250          var cb = typeof a.checked == "boolean";
35251         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35252         var colcls = 'x-t-' + tid + '-c0';
35253         var buf = [
35254             '<li class="x-tree-node">',
35255             
35256                 
35257                 '<div class="x-tree-node-el ', a.cls,'">',
35258                     // extran...
35259                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35260                 
35261                 
35262                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35263                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35264                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35265                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35266                            (a.iconCls ? ' '+a.iconCls : ''),
35267                            '" unselectable="on" />',
35268                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35269                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35270                              
35271                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35272                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35273                             '<span unselectable="on" qtip="' + tx + '">',
35274                              tx,
35275                              '</span></a>' ,
35276                     '</div>',
35277                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35278                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35279                  ];
35280         for(var i = 1, len = cols.length; i < len; i++){
35281             c = cols[i];
35282             colcls = 'x-t-' + tid + '-c' +i;
35283             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35284             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35285                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35286                       "</div>");
35287          }
35288          
35289          buf.push(
35290             '</a>',
35291             '<div class="x-clear"></div></div>',
35292             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35293             "</li>");
35294         
35295         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35296             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35297                                 n.nextSibling.ui.getEl(), buf.join(""));
35298         }else{
35299             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35300         }
35301         var el = this.wrap.firstChild;
35302         this.elRow = el;
35303         this.elNode = el.firstChild;
35304         this.ranchor = el.childNodes[1];
35305         this.ctNode = this.wrap.childNodes[1];
35306         var cs = el.firstChild.childNodes;
35307         this.indentNode = cs[0];
35308         this.ecNode = cs[1];
35309         this.iconNode = cs[2];
35310         var index = 3;
35311         if(cb){
35312             this.checkbox = cs[3];
35313             index++;
35314         }
35315         this.anchor = cs[index];
35316         
35317         this.textNode = cs[index].firstChild;
35318         
35319         //el.on("click", this.onClick, this);
35320         //el.on("dblclick", this.onDblClick, this);
35321         
35322         
35323        // console.log(this);
35324     },
35325     initEvents : function(){
35326         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35327         
35328             
35329         var a = this.ranchor;
35330
35331         var el = Roo.get(a);
35332
35333         if(Roo.isOpera){ // opera render bug ignores the CSS
35334             el.setStyle("text-decoration", "none");
35335         }
35336
35337         el.on("click", this.onClick, this);
35338         el.on("dblclick", this.onDblClick, this);
35339         el.on("contextmenu", this.onContextMenu, this);
35340         
35341     },
35342     
35343     /*onSelectedChange : function(state){
35344         if(state){
35345             this.focus();
35346             this.addClass("x-tree-selected");
35347         }else{
35348             //this.blur();
35349             this.removeClass("x-tree-selected");
35350         }
35351     },*/
35352     addClass : function(cls){
35353         if(this.elRow){
35354             Roo.fly(this.elRow).addClass(cls);
35355         }
35356         
35357     },
35358     
35359     
35360     removeClass : function(cls){
35361         if(this.elRow){
35362             Roo.fly(this.elRow).removeClass(cls);
35363         }
35364     }
35365
35366     
35367     
35368 });//<Script type="text/javascript">
35369
35370 /*
35371  * Based on:
35372  * Ext JS Library 1.1.1
35373  * Copyright(c) 2006-2007, Ext JS, LLC.
35374  *
35375  * Originally Released Under LGPL - original licence link has changed is not relivant.
35376  *
35377  * Fork - LGPL
35378  * <script type="text/javascript">
35379  */
35380  
35381
35382 /**
35383  * @class Roo.tree.ColumnTree
35384  * @extends Roo.data.TreePanel
35385  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35386  * @cfg {int} borderWidth  compined right/left border allowance
35387  * @constructor
35388  * @param {String/HTMLElement/Element} el The container element
35389  * @param {Object} config
35390  */
35391 Roo.tree.ColumnTree =  function(el, config)
35392 {
35393    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35394    this.addEvents({
35395         /**
35396         * @event resize
35397         * Fire this event on a container when it resizes
35398         * @param {int} w Width
35399         * @param {int} h Height
35400         */
35401        "resize" : true
35402     });
35403     this.on('resize', this.onResize, this);
35404 };
35405
35406 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35407     //lines:false,
35408     
35409     
35410     borderWidth: Roo.isBorderBox ? 0 : 2, 
35411     headEls : false,
35412     
35413     render : function(){
35414         // add the header.....
35415        
35416         Roo.tree.ColumnTree.superclass.render.apply(this);
35417         
35418         this.el.addClass('x-column-tree');
35419         
35420         this.headers = this.el.createChild(
35421             {cls:'x-tree-headers'},this.innerCt.dom);
35422    
35423         var cols = this.columns, c;
35424         var totalWidth = 0;
35425         this.headEls = [];
35426         var  len = cols.length;
35427         for(var i = 0; i < len; i++){
35428              c = cols[i];
35429              totalWidth += c.width;
35430             this.headEls.push(this.headers.createChild({
35431                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35432                  cn: {
35433                      cls:'x-tree-hd-text',
35434                      html: c.header
35435                  },
35436                  style:'width:'+(c.width-this.borderWidth)+'px;'
35437              }));
35438         }
35439         this.headers.createChild({cls:'x-clear'});
35440         // prevent floats from wrapping when clipped
35441         this.headers.setWidth(totalWidth);
35442         //this.innerCt.setWidth(totalWidth);
35443         this.innerCt.setStyle({ overflow: 'auto' });
35444         this.onResize(this.width, this.height);
35445              
35446         
35447     },
35448     onResize : function(w,h)
35449     {
35450         this.height = h;
35451         this.width = w;
35452         // resize cols..
35453         this.innerCt.setWidth(this.width);
35454         this.innerCt.setHeight(this.height-20);
35455         
35456         // headers...
35457         var cols = this.columns, c;
35458         var totalWidth = 0;
35459         var expEl = false;
35460         var len = cols.length;
35461         for(var i = 0; i < len; i++){
35462             c = cols[i];
35463             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35464                 // it's the expander..
35465                 expEl  = this.headEls[i];
35466                 continue;
35467             }
35468             totalWidth += c.width;
35469             
35470         }
35471         if (expEl) {
35472             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35473         }
35474         this.headers.setWidth(w-20);
35475
35476         
35477         
35478         
35479     }
35480 });
35481 /*
35482  * Based on:
35483  * Ext JS Library 1.1.1
35484  * Copyright(c) 2006-2007, Ext JS, LLC.
35485  *
35486  * Originally Released Under LGPL - original licence link has changed is not relivant.
35487  *
35488  * Fork - LGPL
35489  * <script type="text/javascript">
35490  */
35491  
35492 /**
35493  * @class Roo.menu.Menu
35494  * @extends Roo.util.Observable
35495  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35496  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35497  * @constructor
35498  * Creates a new Menu
35499  * @param {Object} config Configuration options
35500  */
35501 Roo.menu.Menu = function(config){
35502     Roo.apply(this, config);
35503     this.id = this.id || Roo.id();
35504     this.addEvents({
35505         /**
35506          * @event beforeshow
35507          * Fires before this menu is displayed
35508          * @param {Roo.menu.Menu} this
35509          */
35510         beforeshow : true,
35511         /**
35512          * @event beforehide
35513          * Fires before this menu is hidden
35514          * @param {Roo.menu.Menu} this
35515          */
35516         beforehide : true,
35517         /**
35518          * @event show
35519          * Fires after this menu is displayed
35520          * @param {Roo.menu.Menu} this
35521          */
35522         show : true,
35523         /**
35524          * @event hide
35525          * Fires after this menu is hidden
35526          * @param {Roo.menu.Menu} this
35527          */
35528         hide : true,
35529         /**
35530          * @event click
35531          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35532          * @param {Roo.menu.Menu} this
35533          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35534          * @param {Roo.EventObject} e
35535          */
35536         click : true,
35537         /**
35538          * @event mouseover
35539          * Fires when the mouse is hovering over this menu
35540          * @param {Roo.menu.Menu} this
35541          * @param {Roo.EventObject} e
35542          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35543          */
35544         mouseover : true,
35545         /**
35546          * @event mouseout
35547          * Fires when the mouse exits this menu
35548          * @param {Roo.menu.Menu} this
35549          * @param {Roo.EventObject} e
35550          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35551          */
35552         mouseout : true,
35553         /**
35554          * @event itemclick
35555          * Fires when a menu item contained in this menu is clicked
35556          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35557          * @param {Roo.EventObject} e
35558          */
35559         itemclick: true
35560     });
35561     if (this.registerMenu) {
35562         Roo.menu.MenuMgr.register(this);
35563     }
35564     
35565     var mis = this.items;
35566     this.items = new Roo.util.MixedCollection();
35567     if(mis){
35568         this.add.apply(this, mis);
35569     }
35570 };
35571
35572 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35573     /**
35574      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35575      */
35576     minWidth : 120,
35577     /**
35578      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35579      * for bottom-right shadow (defaults to "sides")
35580      */
35581     shadow : "sides",
35582     /**
35583      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35584      * this menu (defaults to "tl-tr?")
35585      */
35586     subMenuAlign : "tl-tr?",
35587     /**
35588      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35589      * relative to its element of origin (defaults to "tl-bl?")
35590      */
35591     defaultAlign : "tl-bl?",
35592     /**
35593      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35594      */
35595     allowOtherMenus : false,
35596     /**
35597      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35598      */
35599     registerMenu : true,
35600
35601     hidden:true,
35602
35603     // private
35604     render : function(){
35605         if(this.el){
35606             return;
35607         }
35608         var el = this.el = new Roo.Layer({
35609             cls: "x-menu",
35610             shadow:this.shadow,
35611             constrain: false,
35612             parentEl: this.parentEl || document.body,
35613             zindex:15000
35614         });
35615
35616         this.keyNav = new Roo.menu.MenuNav(this);
35617
35618         if(this.plain){
35619             el.addClass("x-menu-plain");
35620         }
35621         if(this.cls){
35622             el.addClass(this.cls);
35623         }
35624         // generic focus element
35625         this.focusEl = el.createChild({
35626             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35627         });
35628         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35629         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35630         
35631         ul.on("mouseover", this.onMouseOver, this);
35632         ul.on("mouseout", this.onMouseOut, this);
35633         this.items.each(function(item){
35634             if (item.hidden) {
35635                 return;
35636             }
35637             
35638             var li = document.createElement("li");
35639             li.className = "x-menu-list-item";
35640             ul.dom.appendChild(li);
35641             item.render(li, this);
35642         }, this);
35643         this.ul = ul;
35644         this.autoWidth();
35645     },
35646
35647     // private
35648     autoWidth : function(){
35649         var el = this.el, ul = this.ul;
35650         if(!el){
35651             return;
35652         }
35653         var w = this.width;
35654         if(w){
35655             el.setWidth(w);
35656         }else if(Roo.isIE){
35657             el.setWidth(this.minWidth);
35658             var t = el.dom.offsetWidth; // force recalc
35659             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35660         }
35661     },
35662
35663     // private
35664     delayAutoWidth : function(){
35665         if(this.rendered){
35666             if(!this.awTask){
35667                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35668             }
35669             this.awTask.delay(20);
35670         }
35671     },
35672
35673     // private
35674     findTargetItem : function(e){
35675         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35676         if(t && t.menuItemId){
35677             return this.items.get(t.menuItemId);
35678         }
35679     },
35680
35681     // private
35682     onClick : function(e){
35683         Roo.log("menu.onClick");
35684         var t = this.findTargetItem(e);
35685         if(!t){
35686             return;
35687         }
35688         Roo.log(e);
35689         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35690             if(t == this.activeItem && t.shouldDeactivate(e)){
35691                 this.activeItem.deactivate();
35692                 delete this.activeItem;
35693                 return;
35694             }
35695             if(t.canActivate){
35696                 this.setActiveItem(t, true);
35697             }
35698             return;
35699             
35700             
35701         }
35702         
35703         t.onClick(e);
35704         this.fireEvent("click", this, t, e);
35705     },
35706
35707     // private
35708     setActiveItem : function(item, autoExpand){
35709         if(item != this.activeItem){
35710             if(this.activeItem){
35711                 this.activeItem.deactivate();
35712             }
35713             this.activeItem = item;
35714             item.activate(autoExpand);
35715         }else if(autoExpand){
35716             item.expandMenu();
35717         }
35718     },
35719
35720     // private
35721     tryActivate : function(start, step){
35722         var items = this.items;
35723         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35724             var item = items.get(i);
35725             if(!item.disabled && item.canActivate){
35726                 this.setActiveItem(item, false);
35727                 return item;
35728             }
35729         }
35730         return false;
35731     },
35732
35733     // private
35734     onMouseOver : function(e){
35735         var t;
35736         if(t = this.findTargetItem(e)){
35737             if(t.canActivate && !t.disabled){
35738                 this.setActiveItem(t, true);
35739             }
35740         }
35741         this.fireEvent("mouseover", this, e, t);
35742     },
35743
35744     // private
35745     onMouseOut : function(e){
35746         var t;
35747         if(t = this.findTargetItem(e)){
35748             if(t == this.activeItem && t.shouldDeactivate(e)){
35749                 this.activeItem.deactivate();
35750                 delete this.activeItem;
35751             }
35752         }
35753         this.fireEvent("mouseout", this, e, t);
35754     },
35755
35756     /**
35757      * Read-only.  Returns true if the menu is currently displayed, else false.
35758      * @type Boolean
35759      */
35760     isVisible : function(){
35761         return this.el && !this.hidden;
35762     },
35763
35764     /**
35765      * Displays this menu relative to another element
35766      * @param {String/HTMLElement/Roo.Element} element The element to align to
35767      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35768      * the element (defaults to this.defaultAlign)
35769      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35770      */
35771     show : function(el, pos, parentMenu){
35772         this.parentMenu = parentMenu;
35773         if(!this.el){
35774             this.render();
35775         }
35776         this.fireEvent("beforeshow", this);
35777         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35778     },
35779
35780     /**
35781      * Displays this menu at a specific xy position
35782      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35783      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35784      */
35785     showAt : function(xy, parentMenu, /* private: */_e){
35786         this.parentMenu = parentMenu;
35787         if(!this.el){
35788             this.render();
35789         }
35790         if(_e !== false){
35791             this.fireEvent("beforeshow", this);
35792             xy = this.el.adjustForConstraints(xy);
35793         }
35794         this.el.setXY(xy);
35795         this.el.show();
35796         this.hidden = false;
35797         this.focus();
35798         this.fireEvent("show", this);
35799     },
35800
35801     focus : function(){
35802         if(!this.hidden){
35803             this.doFocus.defer(50, this);
35804         }
35805     },
35806
35807     doFocus : function(){
35808         if(!this.hidden){
35809             this.focusEl.focus();
35810         }
35811     },
35812
35813     /**
35814      * Hides this menu and optionally all parent menus
35815      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35816      */
35817     hide : function(deep){
35818         if(this.el && this.isVisible()){
35819             this.fireEvent("beforehide", this);
35820             if(this.activeItem){
35821                 this.activeItem.deactivate();
35822                 this.activeItem = null;
35823             }
35824             this.el.hide();
35825             this.hidden = true;
35826             this.fireEvent("hide", this);
35827         }
35828         if(deep === true && this.parentMenu){
35829             this.parentMenu.hide(true);
35830         }
35831     },
35832
35833     /**
35834      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35835      * Any of the following are valid:
35836      * <ul>
35837      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35838      * <li>An HTMLElement object which will be converted to a menu item</li>
35839      * <li>A menu item config object that will be created as a new menu item</li>
35840      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35841      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35842      * </ul>
35843      * Usage:
35844      * <pre><code>
35845 // Create the menu
35846 var menu = new Roo.menu.Menu();
35847
35848 // Create a menu item to add by reference
35849 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35850
35851 // Add a bunch of items at once using different methods.
35852 // Only the last item added will be returned.
35853 var item = menu.add(
35854     menuItem,                // add existing item by ref
35855     'Dynamic Item',          // new TextItem
35856     '-',                     // new separator
35857     { text: 'Config Item' }  // new item by config
35858 );
35859 </code></pre>
35860      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35861      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35862      */
35863     add : function(){
35864         var a = arguments, l = a.length, item;
35865         for(var i = 0; i < l; i++){
35866             var el = a[i];
35867             if ((typeof(el) == "object") && el.xtype && el.xns) {
35868                 el = Roo.factory(el, Roo.menu);
35869             }
35870             
35871             if(el.render){ // some kind of Item
35872                 item = this.addItem(el);
35873             }else if(typeof el == "string"){ // string
35874                 if(el == "separator" || el == "-"){
35875                     item = this.addSeparator();
35876                 }else{
35877                     item = this.addText(el);
35878                 }
35879             }else if(el.tagName || el.el){ // element
35880                 item = this.addElement(el);
35881             }else if(typeof el == "object"){ // must be menu item config?
35882                 item = this.addMenuItem(el);
35883             }
35884         }
35885         return item;
35886     },
35887
35888     /**
35889      * Returns this menu's underlying {@link Roo.Element} object
35890      * @return {Roo.Element} The element
35891      */
35892     getEl : function(){
35893         if(!this.el){
35894             this.render();
35895         }
35896         return this.el;
35897     },
35898
35899     /**
35900      * Adds a separator bar to the menu
35901      * @return {Roo.menu.Item} The menu item that was added
35902      */
35903     addSeparator : function(){
35904         return this.addItem(new Roo.menu.Separator());
35905     },
35906
35907     /**
35908      * Adds an {@link Roo.Element} object to the menu
35909      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35910      * @return {Roo.menu.Item} The menu item that was added
35911      */
35912     addElement : function(el){
35913         return this.addItem(new Roo.menu.BaseItem(el));
35914     },
35915
35916     /**
35917      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35918      * @param {Roo.menu.Item} item The menu item to add
35919      * @return {Roo.menu.Item} The menu item that was added
35920      */
35921     addItem : function(item){
35922         this.items.add(item);
35923         if(this.ul){
35924             var li = document.createElement("li");
35925             li.className = "x-menu-list-item";
35926             this.ul.dom.appendChild(li);
35927             item.render(li, this);
35928             this.delayAutoWidth();
35929         }
35930         return item;
35931     },
35932
35933     /**
35934      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35935      * @param {Object} config A MenuItem config object
35936      * @return {Roo.menu.Item} The menu item that was added
35937      */
35938     addMenuItem : function(config){
35939         if(!(config instanceof Roo.menu.Item)){
35940             if(typeof config.checked == "boolean"){ // must be check menu item config?
35941                 config = new Roo.menu.CheckItem(config);
35942             }else{
35943                 config = new Roo.menu.Item(config);
35944             }
35945         }
35946         return this.addItem(config);
35947     },
35948
35949     /**
35950      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35951      * @param {String} text The text to display in the menu item
35952      * @return {Roo.menu.Item} The menu item that was added
35953      */
35954     addText : function(text){
35955         return this.addItem(new Roo.menu.TextItem({ text : text }));
35956     },
35957
35958     /**
35959      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35960      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35961      * @param {Roo.menu.Item} item The menu item to add
35962      * @return {Roo.menu.Item} The menu item that was added
35963      */
35964     insert : function(index, item){
35965         this.items.insert(index, item);
35966         if(this.ul){
35967             var li = document.createElement("li");
35968             li.className = "x-menu-list-item";
35969             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35970             item.render(li, this);
35971             this.delayAutoWidth();
35972         }
35973         return item;
35974     },
35975
35976     /**
35977      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35978      * @param {Roo.menu.Item} item The menu item to remove
35979      */
35980     remove : function(item){
35981         this.items.removeKey(item.id);
35982         item.destroy();
35983     },
35984
35985     /**
35986      * Removes and destroys all items in the menu
35987      */
35988     removeAll : function(){
35989         var f;
35990         while(f = this.items.first()){
35991             this.remove(f);
35992         }
35993     }
35994 });
35995
35996 // MenuNav is a private utility class used internally by the Menu
35997 Roo.menu.MenuNav = function(menu){
35998     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35999     this.scope = this.menu = menu;
36000 };
36001
36002 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36003     doRelay : function(e, h){
36004         var k = e.getKey();
36005         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36006             this.menu.tryActivate(0, 1);
36007             return false;
36008         }
36009         return h.call(this.scope || this, e, this.menu);
36010     },
36011
36012     up : function(e, m){
36013         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36014             m.tryActivate(m.items.length-1, -1);
36015         }
36016     },
36017
36018     down : function(e, m){
36019         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36020             m.tryActivate(0, 1);
36021         }
36022     },
36023
36024     right : function(e, m){
36025         if(m.activeItem){
36026             m.activeItem.expandMenu(true);
36027         }
36028     },
36029
36030     left : function(e, m){
36031         m.hide();
36032         if(m.parentMenu && m.parentMenu.activeItem){
36033             m.parentMenu.activeItem.activate();
36034         }
36035     },
36036
36037     enter : function(e, m){
36038         if(m.activeItem){
36039             e.stopPropagation();
36040             m.activeItem.onClick(e);
36041             m.fireEvent("click", this, m.activeItem);
36042             return true;
36043         }
36044     }
36045 });/*
36046  * Based on:
36047  * Ext JS Library 1.1.1
36048  * Copyright(c) 2006-2007, Ext JS, LLC.
36049  *
36050  * Originally Released Under LGPL - original licence link has changed is not relivant.
36051  *
36052  * Fork - LGPL
36053  * <script type="text/javascript">
36054  */
36055  
36056 /**
36057  * @class Roo.menu.MenuMgr
36058  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36059  * @singleton
36060  */
36061 Roo.menu.MenuMgr = function(){
36062    var menus, active, groups = {}, attached = false, lastShow = new Date();
36063
36064    // private - called when first menu is created
36065    function init(){
36066        menus = {};
36067        active = new Roo.util.MixedCollection();
36068        Roo.get(document).addKeyListener(27, function(){
36069            if(active.length > 0){
36070                hideAll();
36071            }
36072        });
36073    }
36074
36075    // private
36076    function hideAll(){
36077        if(active && active.length > 0){
36078            var c = active.clone();
36079            c.each(function(m){
36080                m.hide();
36081            });
36082        }
36083    }
36084
36085    // private
36086    function onHide(m){
36087        active.remove(m);
36088        if(active.length < 1){
36089            Roo.get(document).un("mousedown", onMouseDown);
36090            attached = false;
36091        }
36092    }
36093
36094    // private
36095    function onShow(m){
36096        var last = active.last();
36097        lastShow = new Date();
36098        active.add(m);
36099        if(!attached){
36100            Roo.get(document).on("mousedown", onMouseDown);
36101            attached = true;
36102        }
36103        if(m.parentMenu){
36104           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36105           m.parentMenu.activeChild = m;
36106        }else if(last && last.isVisible()){
36107           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36108        }
36109    }
36110
36111    // private
36112    function onBeforeHide(m){
36113        if(m.activeChild){
36114            m.activeChild.hide();
36115        }
36116        if(m.autoHideTimer){
36117            clearTimeout(m.autoHideTimer);
36118            delete m.autoHideTimer;
36119        }
36120    }
36121
36122    // private
36123    function onBeforeShow(m){
36124        var pm = m.parentMenu;
36125        if(!pm && !m.allowOtherMenus){
36126            hideAll();
36127        }else if(pm && pm.activeChild && active != m){
36128            pm.activeChild.hide();
36129        }
36130    }
36131
36132    // private
36133    function onMouseDown(e){
36134        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36135            hideAll();
36136        }
36137    }
36138
36139    // private
36140    function onBeforeCheck(mi, state){
36141        if(state){
36142            var g = groups[mi.group];
36143            for(var i = 0, l = g.length; i < l; i++){
36144                if(g[i] != mi){
36145                    g[i].setChecked(false);
36146                }
36147            }
36148        }
36149    }
36150
36151    return {
36152
36153        /**
36154         * Hides all menus that are currently visible
36155         */
36156        hideAll : function(){
36157             hideAll();  
36158        },
36159
36160        // private
36161        register : function(menu){
36162            if(!menus){
36163                init();
36164            }
36165            menus[menu.id] = menu;
36166            menu.on("beforehide", onBeforeHide);
36167            menu.on("hide", onHide);
36168            menu.on("beforeshow", onBeforeShow);
36169            menu.on("show", onShow);
36170            var g = menu.group;
36171            if(g && menu.events["checkchange"]){
36172                if(!groups[g]){
36173                    groups[g] = [];
36174                }
36175                groups[g].push(menu);
36176                menu.on("checkchange", onCheck);
36177            }
36178        },
36179
36180         /**
36181          * Returns a {@link Roo.menu.Menu} object
36182          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36183          * be used to generate and return a new Menu instance.
36184          */
36185        get : function(menu){
36186            if(typeof menu == "string"){ // menu id
36187                return menus[menu];
36188            }else if(menu.events){  // menu instance
36189                return menu;
36190            }else if(typeof menu.length == 'number'){ // array of menu items?
36191                return new Roo.menu.Menu({items:menu});
36192            }else{ // otherwise, must be a config
36193                return new Roo.menu.Menu(menu);
36194            }
36195        },
36196
36197        // private
36198        unregister : function(menu){
36199            delete menus[menu.id];
36200            menu.un("beforehide", onBeforeHide);
36201            menu.un("hide", onHide);
36202            menu.un("beforeshow", onBeforeShow);
36203            menu.un("show", onShow);
36204            var g = menu.group;
36205            if(g && menu.events["checkchange"]){
36206                groups[g].remove(menu);
36207                menu.un("checkchange", onCheck);
36208            }
36209        },
36210
36211        // private
36212        registerCheckable : function(menuItem){
36213            var g = menuItem.group;
36214            if(g){
36215                if(!groups[g]){
36216                    groups[g] = [];
36217                }
36218                groups[g].push(menuItem);
36219                menuItem.on("beforecheckchange", onBeforeCheck);
36220            }
36221        },
36222
36223        // private
36224        unregisterCheckable : function(menuItem){
36225            var g = menuItem.group;
36226            if(g){
36227                groups[g].remove(menuItem);
36228                menuItem.un("beforecheckchange", onBeforeCheck);
36229            }
36230        }
36231    };
36232 }();/*
36233  * Based on:
36234  * Ext JS Library 1.1.1
36235  * Copyright(c) 2006-2007, Ext JS, LLC.
36236  *
36237  * Originally Released Under LGPL - original licence link has changed is not relivant.
36238  *
36239  * Fork - LGPL
36240  * <script type="text/javascript">
36241  */
36242  
36243
36244 /**
36245  * @class Roo.menu.BaseItem
36246  * @extends Roo.Component
36247  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36248  * management and base configuration options shared by all menu components.
36249  * @constructor
36250  * Creates a new BaseItem
36251  * @param {Object} config Configuration options
36252  */
36253 Roo.menu.BaseItem = function(config){
36254     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36255
36256     this.addEvents({
36257         /**
36258          * @event click
36259          * Fires when this item is clicked
36260          * @param {Roo.menu.BaseItem} this
36261          * @param {Roo.EventObject} e
36262          */
36263         click: true,
36264         /**
36265          * @event activate
36266          * Fires when this item is activated
36267          * @param {Roo.menu.BaseItem} this
36268          */
36269         activate : true,
36270         /**
36271          * @event deactivate
36272          * Fires when this item is deactivated
36273          * @param {Roo.menu.BaseItem} this
36274          */
36275         deactivate : true
36276     });
36277
36278     if(this.handler){
36279         this.on("click", this.handler, this.scope, true);
36280     }
36281 };
36282
36283 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36284     /**
36285      * @cfg {Function} handler
36286      * A function that will handle the click event of this menu item (defaults to undefined)
36287      */
36288     /**
36289      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36290      */
36291     canActivate : false,
36292     
36293      /**
36294      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36295      */
36296     hidden: false,
36297     
36298     /**
36299      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36300      */
36301     activeClass : "x-menu-item-active",
36302     /**
36303      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36304      */
36305     hideOnClick : true,
36306     /**
36307      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36308      */
36309     hideDelay : 100,
36310
36311     // private
36312     ctype: "Roo.menu.BaseItem",
36313
36314     // private
36315     actionMode : "container",
36316
36317     // private
36318     render : function(container, parentMenu){
36319         this.parentMenu = parentMenu;
36320         Roo.menu.BaseItem.superclass.render.call(this, container);
36321         this.container.menuItemId = this.id;
36322     },
36323
36324     // private
36325     onRender : function(container, position){
36326         this.el = Roo.get(this.el);
36327         container.dom.appendChild(this.el.dom);
36328     },
36329
36330     // private
36331     onClick : function(e){
36332         if(!this.disabled && this.fireEvent("click", this, e) !== false
36333                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36334             this.handleClick(e);
36335         }else{
36336             e.stopEvent();
36337         }
36338     },
36339
36340     // private
36341     activate : function(){
36342         if(this.disabled){
36343             return false;
36344         }
36345         var li = this.container;
36346         li.addClass(this.activeClass);
36347         this.region = li.getRegion().adjust(2, 2, -2, -2);
36348         this.fireEvent("activate", this);
36349         return true;
36350     },
36351
36352     // private
36353     deactivate : function(){
36354         this.container.removeClass(this.activeClass);
36355         this.fireEvent("deactivate", this);
36356     },
36357
36358     // private
36359     shouldDeactivate : function(e){
36360         return !this.region || !this.region.contains(e.getPoint());
36361     },
36362
36363     // private
36364     handleClick : function(e){
36365         if(this.hideOnClick){
36366             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36367         }
36368     },
36369
36370     // private
36371     expandMenu : function(autoActivate){
36372         // do nothing
36373     },
36374
36375     // private
36376     hideMenu : function(){
36377         // do nothing
36378     }
36379 });/*
36380  * Based on:
36381  * Ext JS Library 1.1.1
36382  * Copyright(c) 2006-2007, Ext JS, LLC.
36383  *
36384  * Originally Released Under LGPL - original licence link has changed is not relivant.
36385  *
36386  * Fork - LGPL
36387  * <script type="text/javascript">
36388  */
36389  
36390 /**
36391  * @class Roo.menu.Adapter
36392  * @extends Roo.menu.BaseItem
36393  * 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.
36394  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36395  * @constructor
36396  * Creates a new Adapter
36397  * @param {Object} config Configuration options
36398  */
36399 Roo.menu.Adapter = function(component, config){
36400     Roo.menu.Adapter.superclass.constructor.call(this, config);
36401     this.component = component;
36402 };
36403 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36404     // private
36405     canActivate : true,
36406
36407     // private
36408     onRender : function(container, position){
36409         this.component.render(container);
36410         this.el = this.component.getEl();
36411     },
36412
36413     // private
36414     activate : function(){
36415         if(this.disabled){
36416             return false;
36417         }
36418         this.component.focus();
36419         this.fireEvent("activate", this);
36420         return true;
36421     },
36422
36423     // private
36424     deactivate : function(){
36425         this.fireEvent("deactivate", this);
36426     },
36427
36428     // private
36429     disable : function(){
36430         this.component.disable();
36431         Roo.menu.Adapter.superclass.disable.call(this);
36432     },
36433
36434     // private
36435     enable : function(){
36436         this.component.enable();
36437         Roo.menu.Adapter.superclass.enable.call(this);
36438     }
36439 });/*
36440  * Based on:
36441  * Ext JS Library 1.1.1
36442  * Copyright(c) 2006-2007, Ext JS, LLC.
36443  *
36444  * Originally Released Under LGPL - original licence link has changed is not relivant.
36445  *
36446  * Fork - LGPL
36447  * <script type="text/javascript">
36448  */
36449
36450 /**
36451  * @class Roo.menu.TextItem
36452  * @extends Roo.menu.BaseItem
36453  * Adds a static text string to a menu, usually used as either a heading or group separator.
36454  * Note: old style constructor with text is still supported.
36455  * 
36456  * @constructor
36457  * Creates a new TextItem
36458  * @param {Object} cfg Configuration
36459  */
36460 Roo.menu.TextItem = function(cfg){
36461     if (typeof(cfg) == 'string') {
36462         this.text = cfg;
36463     } else {
36464         Roo.apply(this,cfg);
36465     }
36466     
36467     Roo.menu.TextItem.superclass.constructor.call(this);
36468 };
36469
36470 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36471     /**
36472      * @cfg {Boolean} text Text to show on item.
36473      */
36474     text : '',
36475     
36476     /**
36477      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36478      */
36479     hideOnClick : false,
36480     /**
36481      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36482      */
36483     itemCls : "x-menu-text",
36484
36485     // private
36486     onRender : function(){
36487         var s = document.createElement("span");
36488         s.className = this.itemCls;
36489         s.innerHTML = this.text;
36490         this.el = s;
36491         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36492     }
36493 });/*
36494  * Based on:
36495  * Ext JS Library 1.1.1
36496  * Copyright(c) 2006-2007, Ext JS, LLC.
36497  *
36498  * Originally Released Under LGPL - original licence link has changed is not relivant.
36499  *
36500  * Fork - LGPL
36501  * <script type="text/javascript">
36502  */
36503
36504 /**
36505  * @class Roo.menu.Separator
36506  * @extends Roo.menu.BaseItem
36507  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36508  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36509  * @constructor
36510  * @param {Object} config Configuration options
36511  */
36512 Roo.menu.Separator = function(config){
36513     Roo.menu.Separator.superclass.constructor.call(this, config);
36514 };
36515
36516 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36517     /**
36518      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36519      */
36520     itemCls : "x-menu-sep",
36521     /**
36522      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36523      */
36524     hideOnClick : false,
36525
36526     // private
36527     onRender : function(li){
36528         var s = document.createElement("span");
36529         s.className = this.itemCls;
36530         s.innerHTML = "&#160;";
36531         this.el = s;
36532         li.addClass("x-menu-sep-li");
36533         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36534     }
36535 });/*
36536  * Based on:
36537  * Ext JS Library 1.1.1
36538  * Copyright(c) 2006-2007, Ext JS, LLC.
36539  *
36540  * Originally Released Under LGPL - original licence link has changed is not relivant.
36541  *
36542  * Fork - LGPL
36543  * <script type="text/javascript">
36544  */
36545 /**
36546  * @class Roo.menu.Item
36547  * @extends Roo.menu.BaseItem
36548  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36549  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36550  * activation and click handling.
36551  * @constructor
36552  * Creates a new Item
36553  * @param {Object} config Configuration options
36554  */
36555 Roo.menu.Item = function(config){
36556     Roo.menu.Item.superclass.constructor.call(this, config);
36557     if(this.menu){
36558         this.menu = Roo.menu.MenuMgr.get(this.menu);
36559     }
36560 };
36561 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36562     
36563     /**
36564      * @cfg {String} text
36565      * The text to show on the menu item.
36566      */
36567     text: '',
36568      /**
36569      * @cfg {String} HTML to render in menu
36570      * The text to show on the menu item (HTML version).
36571      */
36572     html: '',
36573     /**
36574      * @cfg {String} icon
36575      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36576      */
36577     icon: undefined,
36578     /**
36579      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36580      */
36581     itemCls : "x-menu-item",
36582     /**
36583      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36584      */
36585     canActivate : true,
36586     /**
36587      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36588      */
36589     showDelay: 200,
36590     // doc'd in BaseItem
36591     hideDelay: 200,
36592
36593     // private
36594     ctype: "Roo.menu.Item",
36595     
36596     // private
36597     onRender : function(container, position){
36598         var el = document.createElement("a");
36599         el.hideFocus = true;
36600         el.unselectable = "on";
36601         el.href = this.href || "#";
36602         if(this.hrefTarget){
36603             el.target = this.hrefTarget;
36604         }
36605         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36606         
36607         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36608         
36609         el.innerHTML = String.format(
36610                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36611                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36612         this.el = el;
36613         Roo.menu.Item.superclass.onRender.call(this, container, position);
36614     },
36615
36616     /**
36617      * Sets the text to display in this menu item
36618      * @param {String} text The text to display
36619      * @param {Boolean} isHTML true to indicate text is pure html.
36620      */
36621     setText : function(text, isHTML){
36622         if (isHTML) {
36623             this.html = text;
36624         } else {
36625             this.text = text;
36626             this.html = '';
36627         }
36628         if(this.rendered){
36629             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36630      
36631             this.el.update(String.format(
36632                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36633                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36634             this.parentMenu.autoWidth();
36635         }
36636     },
36637
36638     // private
36639     handleClick : function(e){
36640         if(!this.href){ // if no link defined, stop the event automatically
36641             e.stopEvent();
36642         }
36643         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36644     },
36645
36646     // private
36647     activate : function(autoExpand){
36648         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36649             this.focus();
36650             if(autoExpand){
36651                 this.expandMenu();
36652             }
36653         }
36654         return true;
36655     },
36656
36657     // private
36658     shouldDeactivate : function(e){
36659         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36660             if(this.menu && this.menu.isVisible()){
36661                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36662             }
36663             return true;
36664         }
36665         return false;
36666     },
36667
36668     // private
36669     deactivate : function(){
36670         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36671         this.hideMenu();
36672     },
36673
36674     // private
36675     expandMenu : function(autoActivate){
36676         if(!this.disabled && this.menu){
36677             clearTimeout(this.hideTimer);
36678             delete this.hideTimer;
36679             if(!this.menu.isVisible() && !this.showTimer){
36680                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36681             }else if (this.menu.isVisible() && autoActivate){
36682                 this.menu.tryActivate(0, 1);
36683             }
36684         }
36685     },
36686
36687     // private
36688     deferExpand : function(autoActivate){
36689         delete this.showTimer;
36690         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36691         if(autoActivate){
36692             this.menu.tryActivate(0, 1);
36693         }
36694     },
36695
36696     // private
36697     hideMenu : function(){
36698         clearTimeout(this.showTimer);
36699         delete this.showTimer;
36700         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36701             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36702         }
36703     },
36704
36705     // private
36706     deferHide : function(){
36707         delete this.hideTimer;
36708         this.menu.hide();
36709     }
36710 });/*
36711  * Based on:
36712  * Ext JS Library 1.1.1
36713  * Copyright(c) 2006-2007, Ext JS, LLC.
36714  *
36715  * Originally Released Under LGPL - original licence link has changed is not relivant.
36716  *
36717  * Fork - LGPL
36718  * <script type="text/javascript">
36719  */
36720  
36721 /**
36722  * @class Roo.menu.CheckItem
36723  * @extends Roo.menu.Item
36724  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36725  * @constructor
36726  * Creates a new CheckItem
36727  * @param {Object} config Configuration options
36728  */
36729 Roo.menu.CheckItem = function(config){
36730     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36731     this.addEvents({
36732         /**
36733          * @event beforecheckchange
36734          * Fires before the checked value is set, providing an opportunity to cancel if needed
36735          * @param {Roo.menu.CheckItem} this
36736          * @param {Boolean} checked The new checked value that will be set
36737          */
36738         "beforecheckchange" : true,
36739         /**
36740          * @event checkchange
36741          * Fires after the checked value has been set
36742          * @param {Roo.menu.CheckItem} this
36743          * @param {Boolean} checked The checked value that was set
36744          */
36745         "checkchange" : true
36746     });
36747     if(this.checkHandler){
36748         this.on('checkchange', this.checkHandler, this.scope);
36749     }
36750 };
36751 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36752     /**
36753      * @cfg {String} group
36754      * All check items with the same group name will automatically be grouped into a single-select
36755      * radio button group (defaults to '')
36756      */
36757     /**
36758      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36759      */
36760     itemCls : "x-menu-item x-menu-check-item",
36761     /**
36762      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36763      */
36764     groupClass : "x-menu-group-item",
36765
36766     /**
36767      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36768      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36769      * initialized with checked = true will be rendered as checked.
36770      */
36771     checked: false,
36772
36773     // private
36774     ctype: "Roo.menu.CheckItem",
36775
36776     // private
36777     onRender : function(c){
36778         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36779         if(this.group){
36780             this.el.addClass(this.groupClass);
36781         }
36782         Roo.menu.MenuMgr.registerCheckable(this);
36783         if(this.checked){
36784             this.checked = false;
36785             this.setChecked(true, true);
36786         }
36787     },
36788
36789     // private
36790     destroy : function(){
36791         if(this.rendered){
36792             Roo.menu.MenuMgr.unregisterCheckable(this);
36793         }
36794         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36795     },
36796
36797     /**
36798      * Set the checked state of this item
36799      * @param {Boolean} checked The new checked value
36800      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36801      */
36802     setChecked : function(state, suppressEvent){
36803         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36804             if(this.container){
36805                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36806             }
36807             this.checked = state;
36808             if(suppressEvent !== true){
36809                 this.fireEvent("checkchange", this, state);
36810             }
36811         }
36812     },
36813
36814     // private
36815     handleClick : function(e){
36816        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36817            this.setChecked(!this.checked);
36818        }
36819        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36820     }
36821 });/*
36822  * Based on:
36823  * Ext JS Library 1.1.1
36824  * Copyright(c) 2006-2007, Ext JS, LLC.
36825  *
36826  * Originally Released Under LGPL - original licence link has changed is not relivant.
36827  *
36828  * Fork - LGPL
36829  * <script type="text/javascript">
36830  */
36831  
36832 /**
36833  * @class Roo.menu.DateItem
36834  * @extends Roo.menu.Adapter
36835  * A menu item that wraps the {@link Roo.DatPicker} component.
36836  * @constructor
36837  * Creates a new DateItem
36838  * @param {Object} config Configuration options
36839  */
36840 Roo.menu.DateItem = function(config){
36841     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36842     /** The Roo.DatePicker object @type Roo.DatePicker */
36843     this.picker = this.component;
36844     this.addEvents({select: true});
36845     
36846     this.picker.on("render", function(picker){
36847         picker.getEl().swallowEvent("click");
36848         picker.container.addClass("x-menu-date-item");
36849     });
36850
36851     this.picker.on("select", this.onSelect, this);
36852 };
36853
36854 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36855     // private
36856     onSelect : function(picker, date){
36857         this.fireEvent("select", this, date, picker);
36858         Roo.menu.DateItem.superclass.handleClick.call(this);
36859     }
36860 });/*
36861  * Based on:
36862  * Ext JS Library 1.1.1
36863  * Copyright(c) 2006-2007, Ext JS, LLC.
36864  *
36865  * Originally Released Under LGPL - original licence link has changed is not relivant.
36866  *
36867  * Fork - LGPL
36868  * <script type="text/javascript">
36869  */
36870  
36871 /**
36872  * @class Roo.menu.ColorItem
36873  * @extends Roo.menu.Adapter
36874  * A menu item that wraps the {@link Roo.ColorPalette} component.
36875  * @constructor
36876  * Creates a new ColorItem
36877  * @param {Object} config Configuration options
36878  */
36879 Roo.menu.ColorItem = function(config){
36880     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36881     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36882     this.palette = this.component;
36883     this.relayEvents(this.palette, ["select"]);
36884     if(this.selectHandler){
36885         this.on('select', this.selectHandler, this.scope);
36886     }
36887 };
36888 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36889  * Based on:
36890  * Ext JS Library 1.1.1
36891  * Copyright(c) 2006-2007, Ext JS, LLC.
36892  *
36893  * Originally Released Under LGPL - original licence link has changed is not relivant.
36894  *
36895  * Fork - LGPL
36896  * <script type="text/javascript">
36897  */
36898  
36899
36900 /**
36901  * @class Roo.menu.DateMenu
36902  * @extends Roo.menu.Menu
36903  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36904  * @constructor
36905  * Creates a new DateMenu
36906  * @param {Object} config Configuration options
36907  */
36908 Roo.menu.DateMenu = function(config){
36909     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36910     this.plain = true;
36911     var di = new Roo.menu.DateItem(config);
36912     this.add(di);
36913     /**
36914      * The {@link Roo.DatePicker} instance for this DateMenu
36915      * @type DatePicker
36916      */
36917     this.picker = di.picker;
36918     /**
36919      * @event select
36920      * @param {DatePicker} picker
36921      * @param {Date} date
36922      */
36923     this.relayEvents(di, ["select"]);
36924     this.on('beforeshow', function(){
36925         if(this.picker){
36926             this.picker.hideMonthPicker(false);
36927         }
36928     }, this);
36929 };
36930 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36931     cls:'x-date-menu'
36932 });/*
36933  * Based on:
36934  * Ext JS Library 1.1.1
36935  * Copyright(c) 2006-2007, Ext JS, LLC.
36936  *
36937  * Originally Released Under LGPL - original licence link has changed is not relivant.
36938  *
36939  * Fork - LGPL
36940  * <script type="text/javascript">
36941  */
36942  
36943
36944 /**
36945  * @class Roo.menu.ColorMenu
36946  * @extends Roo.menu.Menu
36947  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36948  * @constructor
36949  * Creates a new ColorMenu
36950  * @param {Object} config Configuration options
36951  */
36952 Roo.menu.ColorMenu = function(config){
36953     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36954     this.plain = true;
36955     var ci = new Roo.menu.ColorItem(config);
36956     this.add(ci);
36957     /**
36958      * The {@link Roo.ColorPalette} instance for this ColorMenu
36959      * @type ColorPalette
36960      */
36961     this.palette = ci.palette;
36962     /**
36963      * @event select
36964      * @param {ColorPalette} palette
36965      * @param {String} color
36966      */
36967     this.relayEvents(ci, ["select"]);
36968 };
36969 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36970  * Based on:
36971  * Ext JS Library 1.1.1
36972  * Copyright(c) 2006-2007, Ext JS, LLC.
36973  *
36974  * Originally Released Under LGPL - original licence link has changed is not relivant.
36975  *
36976  * Fork - LGPL
36977  * <script type="text/javascript">
36978  */
36979  
36980 /**
36981  * @class Roo.form.Field
36982  * @extends Roo.BoxComponent
36983  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36984  * @constructor
36985  * Creates a new Field
36986  * @param {Object} config Configuration options
36987  */
36988 Roo.form.Field = function(config){
36989     Roo.form.Field.superclass.constructor.call(this, config);
36990 };
36991
36992 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36993     /**
36994      * @cfg {String} fieldLabel Label to use when rendering a form.
36995      */
36996        /**
36997      * @cfg {String} qtip Mouse over tip
36998      */
36999      
37000     /**
37001      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37002      */
37003     invalidClass : "x-form-invalid",
37004     /**
37005      * @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")
37006      */
37007     invalidText : "The value in this field is invalid",
37008     /**
37009      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37010      */
37011     focusClass : "x-form-focus",
37012     /**
37013      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37014       automatic validation (defaults to "keyup").
37015      */
37016     validationEvent : "keyup",
37017     /**
37018      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37019      */
37020     validateOnBlur : true,
37021     /**
37022      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37023      */
37024     validationDelay : 250,
37025     /**
37026      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37027      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37028      */
37029     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
37030     /**
37031      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37032      */
37033     fieldClass : "x-form-field",
37034     /**
37035      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37036      *<pre>
37037 Value         Description
37038 -----------   ----------------------------------------------------------------------
37039 qtip          Display a quick tip when the user hovers over the field
37040 title         Display a default browser title attribute popup
37041 under         Add a block div beneath the field containing the error text
37042 side          Add an error icon to the right of the field with a popup on hover
37043 [element id]  Add the error text directly to the innerHTML of the specified element
37044 </pre>
37045      */
37046     msgTarget : 'qtip',
37047     /**
37048      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37049      */
37050     msgFx : 'normal',
37051
37052     /**
37053      * @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.
37054      */
37055     readOnly : false,
37056
37057     /**
37058      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37059      */
37060     disabled : false,
37061
37062     /**
37063      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37064      */
37065     inputType : undefined,
37066     
37067     /**
37068      * @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).
37069          */
37070         tabIndex : undefined,
37071         
37072     // private
37073     isFormField : true,
37074
37075     // private
37076     hasFocus : false,
37077     /**
37078      * @property {Roo.Element} fieldEl
37079      * Element Containing the rendered Field (with label etc.)
37080      */
37081     /**
37082      * @cfg {Mixed} value A value to initialize this field with.
37083      */
37084     value : undefined,
37085
37086     /**
37087      * @cfg {String} name The field's HTML name attribute.
37088      */
37089     /**
37090      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37091      */
37092
37093         // private ??
37094         initComponent : function(){
37095         Roo.form.Field.superclass.initComponent.call(this);
37096         this.addEvents({
37097             /**
37098              * @event focus
37099              * Fires when this field receives input focus.
37100              * @param {Roo.form.Field} this
37101              */
37102             focus : true,
37103             /**
37104              * @event blur
37105              * Fires when this field loses input focus.
37106              * @param {Roo.form.Field} this
37107              */
37108             blur : true,
37109             /**
37110              * @event specialkey
37111              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37112              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37113              * @param {Roo.form.Field} this
37114              * @param {Roo.EventObject} e The event object
37115              */
37116             specialkey : true,
37117             /**
37118              * @event change
37119              * Fires just before the field blurs if the field value has changed.
37120              * @param {Roo.form.Field} this
37121              * @param {Mixed} newValue The new value
37122              * @param {Mixed} oldValue The original value
37123              */
37124             change : true,
37125             /**
37126              * @event invalid
37127              * Fires after the field has been marked as invalid.
37128              * @param {Roo.form.Field} this
37129              * @param {String} msg The validation message
37130              */
37131             invalid : true,
37132             /**
37133              * @event valid
37134              * Fires after the field has been validated with no errors.
37135              * @param {Roo.form.Field} this
37136              */
37137             valid : true,
37138              /**
37139              * @event keyup
37140              * Fires after the key up
37141              * @param {Roo.form.Field} this
37142              * @param {Roo.EventObject}  e The event Object
37143              */
37144             keyup : true
37145         });
37146     },
37147
37148     /**
37149      * Returns the name attribute of the field if available
37150      * @return {String} name The field name
37151      */
37152     getName: function(){
37153          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37154     },
37155
37156     // private
37157     onRender : function(ct, position){
37158         Roo.form.Field.superclass.onRender.call(this, ct, position);
37159         if(!this.el){
37160             var cfg = this.getAutoCreate();
37161             if(!cfg.name){
37162                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37163             }
37164             if (!cfg.name.length) {
37165                 delete cfg.name;
37166             }
37167             if(this.inputType){
37168                 cfg.type = this.inputType;
37169             }
37170             this.el = ct.createChild(cfg, position);
37171         }
37172         var type = this.el.dom.type;
37173         if(type){
37174             if(type == 'password'){
37175                 type = 'text';
37176             }
37177             this.el.addClass('x-form-'+type);
37178         }
37179         if(this.readOnly){
37180             this.el.dom.readOnly = true;
37181         }
37182         if(this.tabIndex !== undefined){
37183             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37184         }
37185
37186         this.el.addClass([this.fieldClass, this.cls]);
37187         this.initValue();
37188     },
37189
37190     /**
37191      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37192      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37193      * @return {Roo.form.Field} this
37194      */
37195     applyTo : function(target){
37196         this.allowDomMove = false;
37197         this.el = Roo.get(target);
37198         this.render(this.el.dom.parentNode);
37199         return this;
37200     },
37201
37202     // private
37203     initValue : function(){
37204         if(this.value !== undefined){
37205             this.setValue(this.value);
37206         }else if(this.el.dom.value.length > 0){
37207             this.setValue(this.el.dom.value);
37208         }
37209     },
37210
37211     /**
37212      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37213      */
37214     isDirty : function() {
37215         if(this.disabled) {
37216             return false;
37217         }
37218         return String(this.getValue()) !== String(this.originalValue);
37219     },
37220
37221     // private
37222     afterRender : function(){
37223         Roo.form.Field.superclass.afterRender.call(this);
37224         this.initEvents();
37225     },
37226
37227     // private
37228     fireKey : function(e){
37229         //Roo.log('field ' + e.getKey());
37230         if(e.isNavKeyPress()){
37231             this.fireEvent("specialkey", this, e);
37232         }
37233     },
37234
37235     /**
37236      * Resets the current field value to the originally loaded value and clears any validation messages
37237      */
37238     reset : function(){
37239         this.setValue(this.resetValue);
37240         this.clearInvalid();
37241     },
37242
37243     // private
37244     initEvents : function(){
37245         // safari killled keypress - so keydown is now used..
37246         this.el.on("keydown" , this.fireKey,  this);
37247         this.el.on("focus", this.onFocus,  this);
37248         this.el.on("blur", this.onBlur,  this);
37249         this.el.relayEvent('keyup', this);
37250
37251         // reference to original value for reset
37252         this.originalValue = this.getValue();
37253         this.resetValue =  this.getValue();
37254     },
37255
37256     // private
37257     onFocus : function(){
37258         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37259             this.el.addClass(this.focusClass);
37260         }
37261         if(!this.hasFocus){
37262             this.hasFocus = true;
37263             this.startValue = this.getValue();
37264             this.fireEvent("focus", this);
37265         }
37266     },
37267
37268     beforeBlur : Roo.emptyFn,
37269
37270     // private
37271     onBlur : function(){
37272         this.beforeBlur();
37273         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37274             this.el.removeClass(this.focusClass);
37275         }
37276         this.hasFocus = false;
37277         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37278             this.validate();
37279         }
37280         var v = this.getValue();
37281         if(String(v) !== String(this.startValue)){
37282             this.fireEvent('change', this, v, this.startValue);
37283         }
37284         this.fireEvent("blur", this);
37285     },
37286
37287     /**
37288      * Returns whether or not the field value is currently valid
37289      * @param {Boolean} preventMark True to disable marking the field invalid
37290      * @return {Boolean} True if the value is valid, else false
37291      */
37292     isValid : function(preventMark){
37293         if(this.disabled){
37294             return true;
37295         }
37296         var restore = this.preventMark;
37297         this.preventMark = preventMark === true;
37298         var v = this.validateValue(this.processValue(this.getRawValue()));
37299         this.preventMark = restore;
37300         return v;
37301     },
37302
37303     /**
37304      * Validates the field value
37305      * @return {Boolean} True if the value is valid, else false
37306      */
37307     validate : function(){
37308         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37309             this.clearInvalid();
37310             return true;
37311         }
37312         return false;
37313     },
37314
37315     processValue : function(value){
37316         return value;
37317     },
37318
37319     // private
37320     // Subclasses should provide the validation implementation by overriding this
37321     validateValue : function(value){
37322         return true;
37323     },
37324
37325     /**
37326      * Mark this field as invalid
37327      * @param {String} msg The validation message
37328      */
37329     markInvalid : function(msg){
37330         if(!this.rendered || this.preventMark){ // not rendered
37331             return;
37332         }
37333         
37334         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37335         
37336         obj.el.addClass(this.invalidClass);
37337         msg = msg || this.invalidText;
37338         switch(this.msgTarget){
37339             case 'qtip':
37340                 obj.el.dom.qtip = msg;
37341                 obj.el.dom.qclass = 'x-form-invalid-tip';
37342                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37343                     Roo.QuickTips.enable();
37344                 }
37345                 break;
37346             case 'title':
37347                 this.el.dom.title = msg;
37348                 break;
37349             case 'under':
37350                 if(!this.errorEl){
37351                     var elp = this.el.findParent('.x-form-element', 5, true);
37352                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37353                     this.errorEl.setWidth(elp.getWidth(true)-20);
37354                 }
37355                 this.errorEl.update(msg);
37356                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37357                 break;
37358             case 'side':
37359                 if(!this.errorIcon){
37360                     var elp = this.el.findParent('.x-form-element', 5, true);
37361                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37362                 }
37363                 this.alignErrorIcon();
37364                 this.errorIcon.dom.qtip = msg;
37365                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37366                 this.errorIcon.show();
37367                 this.on('resize', this.alignErrorIcon, this);
37368                 break;
37369             default:
37370                 var t = Roo.getDom(this.msgTarget);
37371                 t.innerHTML = msg;
37372                 t.style.display = this.msgDisplay;
37373                 break;
37374         }
37375         this.fireEvent('invalid', this, msg);
37376     },
37377
37378     // private
37379     alignErrorIcon : function(){
37380         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37381     },
37382
37383     /**
37384      * Clear any invalid styles/messages for this field
37385      */
37386     clearInvalid : function(){
37387         if(!this.rendered || this.preventMark){ // not rendered
37388             return;
37389         }
37390         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37391         
37392         obj.el.removeClass(this.invalidClass);
37393         switch(this.msgTarget){
37394             case 'qtip':
37395                 obj.el.dom.qtip = '';
37396                 break;
37397             case 'title':
37398                 this.el.dom.title = '';
37399                 break;
37400             case 'under':
37401                 if(this.errorEl){
37402                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37403                 }
37404                 break;
37405             case 'side':
37406                 if(this.errorIcon){
37407                     this.errorIcon.dom.qtip = '';
37408                     this.errorIcon.hide();
37409                     this.un('resize', this.alignErrorIcon, this);
37410                 }
37411                 break;
37412             default:
37413                 var t = Roo.getDom(this.msgTarget);
37414                 t.innerHTML = '';
37415                 t.style.display = 'none';
37416                 break;
37417         }
37418         this.fireEvent('valid', this);
37419     },
37420
37421     /**
37422      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37423      * @return {Mixed} value The field value
37424      */
37425     getRawValue : function(){
37426         var v = this.el.getValue();
37427         
37428         return v;
37429     },
37430
37431     /**
37432      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37433      * @return {Mixed} value The field value
37434      */
37435     getValue : function(){
37436         var v = this.el.getValue();
37437          
37438         return v;
37439     },
37440
37441     /**
37442      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37443      * @param {Mixed} value The value to set
37444      */
37445     setRawValue : function(v){
37446         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37447     },
37448
37449     /**
37450      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37451      * @param {Mixed} value The value to set
37452      */
37453     setValue : function(v){
37454         this.value = v;
37455         if(this.rendered){
37456             this.el.dom.value = (v === null || v === undefined ? '' : v);
37457              this.validate();
37458         }
37459     },
37460
37461     adjustSize : function(w, h){
37462         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37463         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37464         return s;
37465     },
37466
37467     adjustWidth : function(tag, w){
37468         tag = tag.toLowerCase();
37469         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37470             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37471                 if(tag == 'input'){
37472                     return w + 2;
37473                 }
37474                 if(tag == 'textarea'){
37475                     return w-2;
37476                 }
37477             }else if(Roo.isOpera){
37478                 if(tag == 'input'){
37479                     return w + 2;
37480                 }
37481                 if(tag == 'textarea'){
37482                     return w-2;
37483                 }
37484             }
37485         }
37486         return w;
37487     }
37488 });
37489
37490
37491 // anything other than normal should be considered experimental
37492 Roo.form.Field.msgFx = {
37493     normal : {
37494         show: function(msgEl, f){
37495             msgEl.setDisplayed('block');
37496         },
37497
37498         hide : function(msgEl, f){
37499             msgEl.setDisplayed(false).update('');
37500         }
37501     },
37502
37503     slide : {
37504         show: function(msgEl, f){
37505             msgEl.slideIn('t', {stopFx:true});
37506         },
37507
37508         hide : function(msgEl, f){
37509             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37510         }
37511     },
37512
37513     slideRight : {
37514         show: function(msgEl, f){
37515             msgEl.fixDisplay();
37516             msgEl.alignTo(f.el, 'tl-tr');
37517             msgEl.slideIn('l', {stopFx:true});
37518         },
37519
37520         hide : function(msgEl, f){
37521             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37522         }
37523     }
37524 };/*
37525  * Based on:
37526  * Ext JS Library 1.1.1
37527  * Copyright(c) 2006-2007, Ext JS, LLC.
37528  *
37529  * Originally Released Under LGPL - original licence link has changed is not relivant.
37530  *
37531  * Fork - LGPL
37532  * <script type="text/javascript">
37533  */
37534  
37535
37536 /**
37537  * @class Roo.form.TextField
37538  * @extends Roo.form.Field
37539  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37540  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37541  * @constructor
37542  * Creates a new TextField
37543  * @param {Object} config Configuration options
37544  */
37545 Roo.form.TextField = function(config){
37546     Roo.form.TextField.superclass.constructor.call(this, config);
37547     this.addEvents({
37548         /**
37549          * @event autosize
37550          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37551          * according to the default logic, but this event provides a hook for the developer to apply additional
37552          * logic at runtime to resize the field if needed.
37553              * @param {Roo.form.Field} this This text field
37554              * @param {Number} width The new field width
37555              */
37556         autosize : true
37557     });
37558 };
37559
37560 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37561     /**
37562      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37563      */
37564     grow : false,
37565     /**
37566      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37567      */
37568     growMin : 30,
37569     /**
37570      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37571      */
37572     growMax : 800,
37573     /**
37574      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37575      */
37576     vtype : null,
37577     /**
37578      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37579      */
37580     maskRe : null,
37581     /**
37582      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37583      */
37584     disableKeyFilter : false,
37585     /**
37586      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37587      */
37588     allowBlank : true,
37589     /**
37590      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37591      */
37592     minLength : 0,
37593     /**
37594      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37595      */
37596     maxLength : Number.MAX_VALUE,
37597     /**
37598      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37599      */
37600     minLengthText : "The minimum length for this field is {0}",
37601     /**
37602      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37603      */
37604     maxLengthText : "The maximum length for this field is {0}",
37605     /**
37606      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37607      */
37608     selectOnFocus : false,
37609     /**
37610      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37611      */
37612     blankText : "This field is required",
37613     /**
37614      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37615      * If available, this function will be called only after the basic validators all return true, and will be passed the
37616      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37617      */
37618     validator : null,
37619     /**
37620      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37621      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37622      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37623      */
37624     regex : null,
37625     /**
37626      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37627      */
37628     regexText : "",
37629     /**
37630      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37631      */
37632     emptyText : null,
37633    
37634
37635     // private
37636     initEvents : function()
37637     {
37638         if (this.emptyText) {
37639             this.el.attr('placeholder', this.emptyText);
37640         }
37641         
37642         Roo.form.TextField.superclass.initEvents.call(this);
37643         if(this.validationEvent == 'keyup'){
37644             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37645             this.el.on('keyup', this.filterValidation, this);
37646         }
37647         else if(this.validationEvent !== false){
37648             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37649         }
37650         
37651         if(this.selectOnFocus){
37652             this.on("focus", this.preFocus, this);
37653             
37654         }
37655         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37656             this.el.on("keypress", this.filterKeys, this);
37657         }
37658         if(this.grow){
37659             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37660             this.el.on("click", this.autoSize,  this);
37661         }
37662         if(this.el.is('input[type=password]') && Roo.isSafari){
37663             this.el.on('keydown', this.SafariOnKeyDown, this);
37664         }
37665     },
37666
37667     processValue : function(value){
37668         if(this.stripCharsRe){
37669             var newValue = value.replace(this.stripCharsRe, '');
37670             if(newValue !== value){
37671                 this.setRawValue(newValue);
37672                 return newValue;
37673             }
37674         }
37675         return value;
37676     },
37677
37678     filterValidation : function(e){
37679         if(!e.isNavKeyPress()){
37680             this.validationTask.delay(this.validationDelay);
37681         }
37682     },
37683
37684     // private
37685     onKeyUp : function(e){
37686         if(!e.isNavKeyPress()){
37687             this.autoSize();
37688         }
37689     },
37690
37691     /**
37692      * Resets the current field value to the originally-loaded value and clears any validation messages.
37693      *  
37694      */
37695     reset : function(){
37696         Roo.form.TextField.superclass.reset.call(this);
37697        
37698     },
37699
37700     
37701     // private
37702     preFocus : function(){
37703         
37704         if(this.selectOnFocus){
37705             this.el.dom.select();
37706         }
37707     },
37708
37709     
37710     // private
37711     filterKeys : function(e){
37712         var k = e.getKey();
37713         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37714             return;
37715         }
37716         var c = e.getCharCode(), cc = String.fromCharCode(c);
37717         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37718             return;
37719         }
37720         if(!this.maskRe.test(cc)){
37721             e.stopEvent();
37722         }
37723     },
37724
37725     setValue : function(v){
37726         
37727         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37728         
37729         this.autoSize();
37730     },
37731
37732     /**
37733      * Validates a value according to the field's validation rules and marks the field as invalid
37734      * if the validation fails
37735      * @param {Mixed} value The value to validate
37736      * @return {Boolean} True if the value is valid, else false
37737      */
37738     validateValue : function(value){
37739         if(value.length < 1)  { // if it's blank
37740              if(this.allowBlank){
37741                 this.clearInvalid();
37742                 return true;
37743              }else{
37744                 this.markInvalid(this.blankText);
37745                 return false;
37746              }
37747         }
37748         if(value.length < this.minLength){
37749             this.markInvalid(String.format(this.minLengthText, this.minLength));
37750             return false;
37751         }
37752         if(value.length > this.maxLength){
37753             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37754             return false;
37755         }
37756         if(this.vtype){
37757             var vt = Roo.form.VTypes;
37758             if(!vt[this.vtype](value, this)){
37759                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37760                 return false;
37761             }
37762         }
37763         if(typeof this.validator == "function"){
37764             var msg = this.validator(value);
37765             if(msg !== true){
37766                 this.markInvalid(msg);
37767                 return false;
37768             }
37769         }
37770         if(this.regex && !this.regex.test(value)){
37771             this.markInvalid(this.regexText);
37772             return false;
37773         }
37774         return true;
37775     },
37776
37777     /**
37778      * Selects text in this field
37779      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37780      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37781      */
37782     selectText : function(start, end){
37783         var v = this.getRawValue();
37784         if(v.length > 0){
37785             start = start === undefined ? 0 : start;
37786             end = end === undefined ? v.length : end;
37787             var d = this.el.dom;
37788             if(d.setSelectionRange){
37789                 d.setSelectionRange(start, end);
37790             }else if(d.createTextRange){
37791                 var range = d.createTextRange();
37792                 range.moveStart("character", start);
37793                 range.moveEnd("character", v.length-end);
37794                 range.select();
37795             }
37796         }
37797     },
37798
37799     /**
37800      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37801      * This only takes effect if grow = true, and fires the autosize event.
37802      */
37803     autoSize : function(){
37804         if(!this.grow || !this.rendered){
37805             return;
37806         }
37807         if(!this.metrics){
37808             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37809         }
37810         var el = this.el;
37811         var v = el.dom.value;
37812         var d = document.createElement('div');
37813         d.appendChild(document.createTextNode(v));
37814         v = d.innerHTML;
37815         d = null;
37816         v += "&#160;";
37817         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37818         this.el.setWidth(w);
37819         this.fireEvent("autosize", this, w);
37820     },
37821     
37822     // private
37823     SafariOnKeyDown : function(event)
37824     {
37825         // this is a workaround for a password hang bug on chrome/ webkit.
37826         
37827         var isSelectAll = false;
37828         
37829         if(this.el.dom.selectionEnd > 0){
37830             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37831         }
37832         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37833             event.preventDefault();
37834             this.setValue('');
37835             return;
37836         }
37837         
37838         if(isSelectAll){ // backspace and delete key
37839             
37840             event.preventDefault();
37841             // this is very hacky as keydown always get's upper case.
37842             //
37843             var cc = String.fromCharCode(event.getCharCode());
37844             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37845             
37846         }
37847         
37848         
37849     }
37850 });/*
37851  * Based on:
37852  * Ext JS Library 1.1.1
37853  * Copyright(c) 2006-2007, Ext JS, LLC.
37854  *
37855  * Originally Released Under LGPL - original licence link has changed is not relivant.
37856  *
37857  * Fork - LGPL
37858  * <script type="text/javascript">
37859  */
37860  
37861 /**
37862  * @class Roo.form.Hidden
37863  * @extends Roo.form.TextField
37864  * Simple Hidden element used on forms 
37865  * 
37866  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37867  * 
37868  * @constructor
37869  * Creates a new Hidden form element.
37870  * @param {Object} config Configuration options
37871  */
37872
37873
37874
37875 // easy hidden field...
37876 Roo.form.Hidden = function(config){
37877     Roo.form.Hidden.superclass.constructor.call(this, config);
37878 };
37879   
37880 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37881     fieldLabel:      '',
37882     inputType:      'hidden',
37883     width:          50,
37884     allowBlank:     true,
37885     labelSeparator: '',
37886     hidden:         true,
37887     itemCls :       'x-form-item-display-none'
37888
37889
37890 });
37891
37892
37893 /*
37894  * Based on:
37895  * Ext JS Library 1.1.1
37896  * Copyright(c) 2006-2007, Ext JS, LLC.
37897  *
37898  * Originally Released Under LGPL - original licence link has changed is not relivant.
37899  *
37900  * Fork - LGPL
37901  * <script type="text/javascript">
37902  */
37903  
37904 /**
37905  * @class Roo.form.TriggerField
37906  * @extends Roo.form.TextField
37907  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37908  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37909  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37910  * for which you can provide a custom implementation.  For example:
37911  * <pre><code>
37912 var trigger = new Roo.form.TriggerField();
37913 trigger.onTriggerClick = myTriggerFn;
37914 trigger.applyTo('my-field');
37915 </code></pre>
37916  *
37917  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37918  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37919  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37920  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37921  * @constructor
37922  * Create a new TriggerField.
37923  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37924  * to the base TextField)
37925  */
37926 Roo.form.TriggerField = function(config){
37927     this.mimicing = false;
37928     Roo.form.TriggerField.superclass.constructor.call(this, config);
37929 };
37930
37931 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37932     /**
37933      * @cfg {String} triggerClass A CSS class to apply to the trigger
37934      */
37935     /**
37936      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37937      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37938      */
37939     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37940     /**
37941      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37942      */
37943     hideTrigger:false,
37944
37945     /** @cfg {Boolean} grow @hide */
37946     /** @cfg {Number} growMin @hide */
37947     /** @cfg {Number} growMax @hide */
37948
37949     /**
37950      * @hide 
37951      * @method
37952      */
37953     autoSize: Roo.emptyFn,
37954     // private
37955     monitorTab : true,
37956     // private
37957     deferHeight : true,
37958
37959     
37960     actionMode : 'wrap',
37961     // private
37962     onResize : function(w, h){
37963         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37964         if(typeof w == 'number'){
37965             var x = w - this.trigger.getWidth();
37966             this.el.setWidth(this.adjustWidth('input', x));
37967             this.trigger.setStyle('left', x+'px');
37968         }
37969     },
37970
37971     // private
37972     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37973
37974     // private
37975     getResizeEl : function(){
37976         return this.wrap;
37977     },
37978
37979     // private
37980     getPositionEl : function(){
37981         return this.wrap;
37982     },
37983
37984     // private
37985     alignErrorIcon : function(){
37986         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37987     },
37988
37989     // private
37990     onRender : function(ct, position){
37991         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37992         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37993         this.trigger = this.wrap.createChild(this.triggerConfig ||
37994                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37995         if(this.hideTrigger){
37996             this.trigger.setDisplayed(false);
37997         }
37998         this.initTrigger();
37999         if(!this.width){
38000             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38001         }
38002     },
38003
38004     // private
38005     initTrigger : function(){
38006         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38007         this.trigger.addClassOnOver('x-form-trigger-over');
38008         this.trigger.addClassOnClick('x-form-trigger-click');
38009     },
38010
38011     // private
38012     onDestroy : function(){
38013         if(this.trigger){
38014             this.trigger.removeAllListeners();
38015             this.trigger.remove();
38016         }
38017         if(this.wrap){
38018             this.wrap.remove();
38019         }
38020         Roo.form.TriggerField.superclass.onDestroy.call(this);
38021     },
38022
38023     // private
38024     onFocus : function(){
38025         Roo.form.TriggerField.superclass.onFocus.call(this);
38026         if(!this.mimicing){
38027             this.wrap.addClass('x-trigger-wrap-focus');
38028             this.mimicing = true;
38029             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38030             if(this.monitorTab){
38031                 this.el.on("keydown", this.checkTab, this);
38032             }
38033         }
38034     },
38035
38036     // private
38037     checkTab : function(e){
38038         if(e.getKey() == e.TAB){
38039             this.triggerBlur();
38040         }
38041     },
38042
38043     // private
38044     onBlur : function(){
38045         // do nothing
38046     },
38047
38048     // private
38049     mimicBlur : function(e, t){
38050         if(!this.wrap.contains(t) && this.validateBlur()){
38051             this.triggerBlur();
38052         }
38053     },
38054
38055     // private
38056     triggerBlur : function(){
38057         this.mimicing = false;
38058         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38059         if(this.monitorTab){
38060             this.el.un("keydown", this.checkTab, this);
38061         }
38062         this.wrap.removeClass('x-trigger-wrap-focus');
38063         Roo.form.TriggerField.superclass.onBlur.call(this);
38064     },
38065
38066     // private
38067     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38068     validateBlur : function(e, t){
38069         return true;
38070     },
38071
38072     // private
38073     onDisable : function(){
38074         Roo.form.TriggerField.superclass.onDisable.call(this);
38075         if(this.wrap){
38076             this.wrap.addClass('x-item-disabled');
38077         }
38078     },
38079
38080     // private
38081     onEnable : function(){
38082         Roo.form.TriggerField.superclass.onEnable.call(this);
38083         if(this.wrap){
38084             this.wrap.removeClass('x-item-disabled');
38085         }
38086     },
38087
38088     // private
38089     onShow : function(){
38090         var ae = this.getActionEl();
38091         
38092         if(ae){
38093             ae.dom.style.display = '';
38094             ae.dom.style.visibility = 'visible';
38095         }
38096     },
38097
38098     // private
38099     
38100     onHide : function(){
38101         var ae = this.getActionEl();
38102         ae.dom.style.display = 'none';
38103     },
38104
38105     /**
38106      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38107      * by an implementing function.
38108      * @method
38109      * @param {EventObject} e
38110      */
38111     onTriggerClick : Roo.emptyFn
38112 });
38113
38114 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38115 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38116 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38117 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38118     initComponent : function(){
38119         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38120
38121         this.triggerConfig = {
38122             tag:'span', cls:'x-form-twin-triggers', cn:[
38123             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38124             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38125         ]};
38126     },
38127
38128     getTrigger : function(index){
38129         return this.triggers[index];
38130     },
38131
38132     initTrigger : function(){
38133         var ts = this.trigger.select('.x-form-trigger', true);
38134         this.wrap.setStyle('overflow', 'hidden');
38135         var triggerField = this;
38136         ts.each(function(t, all, index){
38137             t.hide = function(){
38138                 var w = triggerField.wrap.getWidth();
38139                 this.dom.style.display = 'none';
38140                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38141             };
38142             t.show = function(){
38143                 var w = triggerField.wrap.getWidth();
38144                 this.dom.style.display = '';
38145                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38146             };
38147             var triggerIndex = 'Trigger'+(index+1);
38148
38149             if(this['hide'+triggerIndex]){
38150                 t.dom.style.display = 'none';
38151             }
38152             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38153             t.addClassOnOver('x-form-trigger-over');
38154             t.addClassOnClick('x-form-trigger-click');
38155         }, this);
38156         this.triggers = ts.elements;
38157     },
38158
38159     onTrigger1Click : Roo.emptyFn,
38160     onTrigger2Click : Roo.emptyFn
38161 });/*
38162  * Based on:
38163  * Ext JS Library 1.1.1
38164  * Copyright(c) 2006-2007, Ext JS, LLC.
38165  *
38166  * Originally Released Under LGPL - original licence link has changed is not relivant.
38167  *
38168  * Fork - LGPL
38169  * <script type="text/javascript">
38170  */
38171  
38172 /**
38173  * @class Roo.form.TextArea
38174  * @extends Roo.form.TextField
38175  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38176  * support for auto-sizing.
38177  * @constructor
38178  * Creates a new TextArea
38179  * @param {Object} config Configuration options
38180  */
38181 Roo.form.TextArea = function(config){
38182     Roo.form.TextArea.superclass.constructor.call(this, config);
38183     // these are provided exchanges for backwards compat
38184     // minHeight/maxHeight were replaced by growMin/growMax to be
38185     // compatible with TextField growing config values
38186     if(this.minHeight !== undefined){
38187         this.growMin = this.minHeight;
38188     }
38189     if(this.maxHeight !== undefined){
38190         this.growMax = this.maxHeight;
38191     }
38192 };
38193
38194 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38195     /**
38196      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38197      */
38198     growMin : 60,
38199     /**
38200      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38201      */
38202     growMax: 1000,
38203     /**
38204      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38205      * in the field (equivalent to setting overflow: hidden, defaults to false)
38206      */
38207     preventScrollbars: false,
38208     /**
38209      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38210      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38211      */
38212
38213     // private
38214     onRender : function(ct, position){
38215         if(!this.el){
38216             this.defaultAutoCreate = {
38217                 tag: "textarea",
38218                 style:"width:300px;height:60px;",
38219                 autocomplete: "off"
38220             };
38221         }
38222         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38223         if(this.grow){
38224             this.textSizeEl = Roo.DomHelper.append(document.body, {
38225                 tag: "pre", cls: "x-form-grow-sizer"
38226             });
38227             if(this.preventScrollbars){
38228                 this.el.setStyle("overflow", "hidden");
38229             }
38230             this.el.setHeight(this.growMin);
38231         }
38232     },
38233
38234     onDestroy : function(){
38235         if(this.textSizeEl){
38236             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38237         }
38238         Roo.form.TextArea.superclass.onDestroy.call(this);
38239     },
38240
38241     // private
38242     onKeyUp : function(e){
38243         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38244             this.autoSize();
38245         }
38246     },
38247
38248     /**
38249      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38250      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38251      */
38252     autoSize : function(){
38253         if(!this.grow || !this.textSizeEl){
38254             return;
38255         }
38256         var el = this.el;
38257         var v = el.dom.value;
38258         var ts = this.textSizeEl;
38259
38260         ts.innerHTML = '';
38261         ts.appendChild(document.createTextNode(v));
38262         v = ts.innerHTML;
38263
38264         Roo.fly(ts).setWidth(this.el.getWidth());
38265         if(v.length < 1){
38266             v = "&#160;&#160;";
38267         }else{
38268             if(Roo.isIE){
38269                 v = v.replace(/\n/g, '<p>&#160;</p>');
38270             }
38271             v += "&#160;\n&#160;";
38272         }
38273         ts.innerHTML = v;
38274         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38275         if(h != this.lastHeight){
38276             this.lastHeight = h;
38277             this.el.setHeight(h);
38278             this.fireEvent("autosize", this, h);
38279         }
38280     }
38281 });/*
38282  * Based on:
38283  * Ext JS Library 1.1.1
38284  * Copyright(c) 2006-2007, Ext JS, LLC.
38285  *
38286  * Originally Released Under LGPL - original licence link has changed is not relivant.
38287  *
38288  * Fork - LGPL
38289  * <script type="text/javascript">
38290  */
38291  
38292
38293 /**
38294  * @class Roo.form.NumberField
38295  * @extends Roo.form.TextField
38296  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38297  * @constructor
38298  * Creates a new NumberField
38299  * @param {Object} config Configuration options
38300  */
38301 Roo.form.NumberField = function(config){
38302     Roo.form.NumberField.superclass.constructor.call(this, config);
38303 };
38304
38305 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38306     /**
38307      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38308      */
38309     fieldClass: "x-form-field x-form-num-field",
38310     /**
38311      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38312      */
38313     allowDecimals : true,
38314     /**
38315      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38316      */
38317     decimalSeparator : ".",
38318     /**
38319      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38320      */
38321     decimalPrecision : 2,
38322     /**
38323      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38324      */
38325     allowNegative : true,
38326     /**
38327      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38328      */
38329     minValue : Number.NEGATIVE_INFINITY,
38330     /**
38331      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38332      */
38333     maxValue : Number.MAX_VALUE,
38334     /**
38335      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38336      */
38337     minText : "The minimum value for this field is {0}",
38338     /**
38339      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38340      */
38341     maxText : "The maximum value for this field is {0}",
38342     /**
38343      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38344      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38345      */
38346     nanText : "{0} is not a valid number",
38347
38348     // private
38349     initEvents : function(){
38350         Roo.form.NumberField.superclass.initEvents.call(this);
38351         var allowed = "0123456789";
38352         if(this.allowDecimals){
38353             allowed += this.decimalSeparator;
38354         }
38355         if(this.allowNegative){
38356             allowed += "-";
38357         }
38358         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38359         var keyPress = function(e){
38360             var k = e.getKey();
38361             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38362                 return;
38363             }
38364             var c = e.getCharCode();
38365             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38366                 e.stopEvent();
38367             }
38368         };
38369         this.el.on("keypress", keyPress, this);
38370     },
38371
38372     // private
38373     validateValue : function(value){
38374         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38375             return false;
38376         }
38377         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38378              return true;
38379         }
38380         var num = this.parseValue(value);
38381         if(isNaN(num)){
38382             this.markInvalid(String.format(this.nanText, value));
38383             return false;
38384         }
38385         if(num < this.minValue){
38386             this.markInvalid(String.format(this.minText, this.minValue));
38387             return false;
38388         }
38389         if(num > this.maxValue){
38390             this.markInvalid(String.format(this.maxText, this.maxValue));
38391             return false;
38392         }
38393         return true;
38394     },
38395
38396     getValue : function(){
38397         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38398     },
38399
38400     // private
38401     parseValue : function(value){
38402         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38403         return isNaN(value) ? '' : value;
38404     },
38405
38406     // private
38407     fixPrecision : function(value){
38408         var nan = isNaN(value);
38409         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38410             return nan ? '' : value;
38411         }
38412         return parseFloat(value).toFixed(this.decimalPrecision);
38413     },
38414
38415     setValue : function(v){
38416         v = this.fixPrecision(v);
38417         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38418     },
38419
38420     // private
38421     decimalPrecisionFcn : function(v){
38422         return Math.floor(v);
38423     },
38424
38425     beforeBlur : function(){
38426         var v = this.parseValue(this.getRawValue());
38427         if(v){
38428             this.setValue(v);
38429         }
38430     }
38431 });/*
38432  * Based on:
38433  * Ext JS Library 1.1.1
38434  * Copyright(c) 2006-2007, Ext JS, LLC.
38435  *
38436  * Originally Released Under LGPL - original licence link has changed is not relivant.
38437  *
38438  * Fork - LGPL
38439  * <script type="text/javascript">
38440  */
38441  
38442 /**
38443  * @class Roo.form.DateField
38444  * @extends Roo.form.TriggerField
38445  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38446 * @constructor
38447 * Create a new DateField
38448 * @param {Object} config
38449  */
38450 Roo.form.DateField = function(config){
38451     Roo.form.DateField.superclass.constructor.call(this, config);
38452     
38453       this.addEvents({
38454          
38455         /**
38456          * @event select
38457          * Fires when a date is selected
38458              * @param {Roo.form.DateField} combo This combo box
38459              * @param {Date} date The date selected
38460              */
38461         'select' : true
38462          
38463     });
38464     
38465     
38466     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38467     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38468     this.ddMatch = null;
38469     if(this.disabledDates){
38470         var dd = this.disabledDates;
38471         var re = "(?:";
38472         for(var i = 0; i < dd.length; i++){
38473             re += dd[i];
38474             if(i != dd.length-1) re += "|";
38475         }
38476         this.ddMatch = new RegExp(re + ")");
38477     }
38478 };
38479
38480 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38481     /**
38482      * @cfg {String} format
38483      * The default date format string which can be overriden for localization support.  The format must be
38484      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38485      */
38486     format : "m/d/y",
38487     /**
38488      * @cfg {String} altFormats
38489      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38490      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38491      */
38492     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38493     /**
38494      * @cfg {Array} disabledDays
38495      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38496      */
38497     disabledDays : null,
38498     /**
38499      * @cfg {String} disabledDaysText
38500      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38501      */
38502     disabledDaysText : "Disabled",
38503     /**
38504      * @cfg {Array} disabledDates
38505      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38506      * expression so they are very powerful. Some examples:
38507      * <ul>
38508      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38509      * <li>["03/08", "09/16"] would disable those days for every year</li>
38510      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38511      * <li>["03/../2006"] would disable every day in March 2006</li>
38512      * <li>["^03"] would disable every day in every March</li>
38513      * </ul>
38514      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38515      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38516      */
38517     disabledDates : null,
38518     /**
38519      * @cfg {String} disabledDatesText
38520      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38521      */
38522     disabledDatesText : "Disabled",
38523     /**
38524      * @cfg {Date/String} minValue
38525      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38526      * valid format (defaults to null).
38527      */
38528     minValue : null,
38529     /**
38530      * @cfg {Date/String} maxValue
38531      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38532      * valid format (defaults to null).
38533      */
38534     maxValue : null,
38535     /**
38536      * @cfg {String} minText
38537      * The error text to display when the date in the cell is before minValue (defaults to
38538      * 'The date in this field must be after {minValue}').
38539      */
38540     minText : "The date in this field must be equal to or after {0}",
38541     /**
38542      * @cfg {String} maxText
38543      * The error text to display when the date in the cell is after maxValue (defaults to
38544      * 'The date in this field must be before {maxValue}').
38545      */
38546     maxText : "The date in this field must be equal to or before {0}",
38547     /**
38548      * @cfg {String} invalidText
38549      * The error text to display when the date in the field is invalid (defaults to
38550      * '{value} is not a valid date - it must be in the format {format}').
38551      */
38552     invalidText : "{0} is not a valid date - it must be in the format {1}",
38553     /**
38554      * @cfg {String} triggerClass
38555      * An additional CSS class used to style the trigger button.  The trigger will always get the
38556      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38557      * which displays a calendar icon).
38558      */
38559     triggerClass : 'x-form-date-trigger',
38560     
38561
38562     /**
38563      * @cfg {Boolean} useIso
38564      * if enabled, then the date field will use a hidden field to store the 
38565      * real value as iso formated date. default (false)
38566      */ 
38567     useIso : false,
38568     /**
38569      * @cfg {String/Object} autoCreate
38570      * A DomHelper element spec, or true for a default element spec (defaults to
38571      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38572      */ 
38573     // private
38574     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38575     
38576     // private
38577     hiddenField: false,
38578     
38579     onRender : function(ct, position)
38580     {
38581         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38582         if (this.useIso) {
38583             //this.el.dom.removeAttribute('name'); 
38584             Roo.log("Changing name?");
38585             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38586             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38587                     'before', true);
38588             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38589             // prevent input submission
38590             this.hiddenName = this.name;
38591         }
38592             
38593             
38594     },
38595     
38596     // private
38597     validateValue : function(value)
38598     {
38599         value = this.formatDate(value);
38600         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38601             Roo.log('super failed');
38602             return false;
38603         }
38604         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38605              return true;
38606         }
38607         var svalue = value;
38608         value = this.parseDate(value);
38609         if(!value){
38610             Roo.log('parse date failed' + svalue);
38611             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38612             return false;
38613         }
38614         var time = value.getTime();
38615         if(this.minValue && time < this.minValue.getTime()){
38616             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38617             return false;
38618         }
38619         if(this.maxValue && time > this.maxValue.getTime()){
38620             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38621             return false;
38622         }
38623         if(this.disabledDays){
38624             var day = value.getDay();
38625             for(var i = 0; i < this.disabledDays.length; i++) {
38626                 if(day === this.disabledDays[i]){
38627                     this.markInvalid(this.disabledDaysText);
38628                     return false;
38629                 }
38630             }
38631         }
38632         var fvalue = this.formatDate(value);
38633         if(this.ddMatch && this.ddMatch.test(fvalue)){
38634             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38635             return false;
38636         }
38637         return true;
38638     },
38639
38640     // private
38641     // Provides logic to override the default TriggerField.validateBlur which just returns true
38642     validateBlur : function(){
38643         return !this.menu || !this.menu.isVisible();
38644     },
38645     
38646     getName: function()
38647     {
38648         // returns hidden if it's set..
38649         if (!this.rendered) {return ''};
38650         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38651         
38652     },
38653
38654     /**
38655      * Returns the current date value of the date field.
38656      * @return {Date} The date value
38657      */
38658     getValue : function(){
38659         
38660         return  this.hiddenField ?
38661                 this.hiddenField.value :
38662                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38663     },
38664
38665     /**
38666      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38667      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38668      * (the default format used is "m/d/y").
38669      * <br />Usage:
38670      * <pre><code>
38671 //All of these calls set the same date value (May 4, 2006)
38672
38673 //Pass a date object:
38674 var dt = new Date('5/4/06');
38675 dateField.setValue(dt);
38676
38677 //Pass a date string (default format):
38678 dateField.setValue('5/4/06');
38679
38680 //Pass a date string (custom format):
38681 dateField.format = 'Y-m-d';
38682 dateField.setValue('2006-5-4');
38683 </code></pre>
38684      * @param {String/Date} date The date or valid date string
38685      */
38686     setValue : function(date){
38687         if (this.hiddenField) {
38688             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38689         }
38690         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38691         // make sure the value field is always stored as a date..
38692         this.value = this.parseDate(date);
38693         
38694         
38695     },
38696
38697     // private
38698     parseDate : function(value){
38699         if(!value || value instanceof Date){
38700             return value;
38701         }
38702         var v = Date.parseDate(value, this.format);
38703          if (!v && this.useIso) {
38704             v = Date.parseDate(value, 'Y-m-d');
38705         }
38706         if(!v && this.altFormats){
38707             if(!this.altFormatsArray){
38708                 this.altFormatsArray = this.altFormats.split("|");
38709             }
38710             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38711                 v = Date.parseDate(value, this.altFormatsArray[i]);
38712             }
38713         }
38714         return v;
38715     },
38716
38717     // private
38718     formatDate : function(date, fmt){
38719         return (!date || !(date instanceof Date)) ?
38720                date : date.dateFormat(fmt || this.format);
38721     },
38722
38723     // private
38724     menuListeners : {
38725         select: function(m, d){
38726             
38727             this.setValue(d);
38728             this.fireEvent('select', this, d);
38729         },
38730         show : function(){ // retain focus styling
38731             this.onFocus();
38732         },
38733         hide : function(){
38734             this.focus.defer(10, this);
38735             var ml = this.menuListeners;
38736             this.menu.un("select", ml.select,  this);
38737             this.menu.un("show", ml.show,  this);
38738             this.menu.un("hide", ml.hide,  this);
38739         }
38740     },
38741
38742     // private
38743     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38744     onTriggerClick : function(){
38745         if(this.disabled){
38746             return;
38747         }
38748         if(this.menu == null){
38749             this.menu = new Roo.menu.DateMenu();
38750         }
38751         Roo.apply(this.menu.picker,  {
38752             showClear: this.allowBlank,
38753             minDate : this.minValue,
38754             maxDate : this.maxValue,
38755             disabledDatesRE : this.ddMatch,
38756             disabledDatesText : this.disabledDatesText,
38757             disabledDays : this.disabledDays,
38758             disabledDaysText : this.disabledDaysText,
38759             format : this.useIso ? 'Y-m-d' : this.format,
38760             minText : String.format(this.minText, this.formatDate(this.minValue)),
38761             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38762         });
38763         this.menu.on(Roo.apply({}, this.menuListeners, {
38764             scope:this
38765         }));
38766         this.menu.picker.setValue(this.getValue() || new Date());
38767         this.menu.show(this.el, "tl-bl?");
38768     },
38769
38770     beforeBlur : function(){
38771         var v = this.parseDate(this.getRawValue());
38772         if(v){
38773             this.setValue(v);
38774         }
38775     },
38776
38777     /*@
38778      * overide
38779      * 
38780      */
38781     isDirty : function() {
38782         if(this.disabled) {
38783             return false;
38784         }
38785         
38786         if(typeof(this.startValue) === 'undefined'){
38787             return false;
38788         }
38789         
38790         return String(this.getValue()) !== String(this.startValue);
38791         
38792     }
38793 });/*
38794  * Based on:
38795  * Ext JS Library 1.1.1
38796  * Copyright(c) 2006-2007, Ext JS, LLC.
38797  *
38798  * Originally Released Under LGPL - original licence link has changed is not relivant.
38799  *
38800  * Fork - LGPL
38801  * <script type="text/javascript">
38802  */
38803  
38804 /**
38805  * @class Roo.form.MonthField
38806  * @extends Roo.form.TriggerField
38807  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38808 * @constructor
38809 * Create a new MonthField
38810 * @param {Object} config
38811  */
38812 Roo.form.MonthField = function(config){
38813     
38814     Roo.form.MonthField.superclass.constructor.call(this, config);
38815     
38816       this.addEvents({
38817          
38818         /**
38819          * @event select
38820          * Fires when a date is selected
38821              * @param {Roo.form.MonthFieeld} combo This combo box
38822              * @param {Date} date The date selected
38823              */
38824         'select' : true
38825          
38826     });
38827     
38828     
38829     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38830     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38831     this.ddMatch = null;
38832     if(this.disabledDates){
38833         var dd = this.disabledDates;
38834         var re = "(?:";
38835         for(var i = 0; i < dd.length; i++){
38836             re += dd[i];
38837             if(i != dd.length-1) re += "|";
38838         }
38839         this.ddMatch = new RegExp(re + ")");
38840     }
38841 };
38842
38843 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38844     /**
38845      * @cfg {String} format
38846      * The default date format string which can be overriden for localization support.  The format must be
38847      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38848      */
38849     format : "M Y",
38850     /**
38851      * @cfg {String} altFormats
38852      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38853      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38854      */
38855     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38856     /**
38857      * @cfg {Array} disabledDays
38858      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38859      */
38860     disabledDays : [0,1,2,3,4,5,6],
38861     /**
38862      * @cfg {String} disabledDaysText
38863      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38864      */
38865     disabledDaysText : "Disabled",
38866     /**
38867      * @cfg {Array} disabledDates
38868      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38869      * expression so they are very powerful. Some examples:
38870      * <ul>
38871      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38872      * <li>["03/08", "09/16"] would disable those days for every year</li>
38873      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38874      * <li>["03/../2006"] would disable every day in March 2006</li>
38875      * <li>["^03"] would disable every day in every March</li>
38876      * </ul>
38877      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38878      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38879      */
38880     disabledDates : null,
38881     /**
38882      * @cfg {String} disabledDatesText
38883      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38884      */
38885     disabledDatesText : "Disabled",
38886     /**
38887      * @cfg {Date/String} minValue
38888      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38889      * valid format (defaults to null).
38890      */
38891     minValue : null,
38892     /**
38893      * @cfg {Date/String} maxValue
38894      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38895      * valid format (defaults to null).
38896      */
38897     maxValue : null,
38898     /**
38899      * @cfg {String} minText
38900      * The error text to display when the date in the cell is before minValue (defaults to
38901      * 'The date in this field must be after {minValue}').
38902      */
38903     minText : "The date in this field must be equal to or after {0}",
38904     /**
38905      * @cfg {String} maxTextf
38906      * The error text to display when the date in the cell is after maxValue (defaults to
38907      * 'The date in this field must be before {maxValue}').
38908      */
38909     maxText : "The date in this field must be equal to or before {0}",
38910     /**
38911      * @cfg {String} invalidText
38912      * The error text to display when the date in the field is invalid (defaults to
38913      * '{value} is not a valid date - it must be in the format {format}').
38914      */
38915     invalidText : "{0} is not a valid date - it must be in the format {1}",
38916     /**
38917      * @cfg {String} triggerClass
38918      * An additional CSS class used to style the trigger button.  The trigger will always get the
38919      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38920      * which displays a calendar icon).
38921      */
38922     triggerClass : 'x-form-date-trigger',
38923     
38924
38925     /**
38926      * @cfg {Boolean} useIso
38927      * if enabled, then the date field will use a hidden field to store the 
38928      * real value as iso formated date. default (true)
38929      */ 
38930     useIso : true,
38931     /**
38932      * @cfg {String/Object} autoCreate
38933      * A DomHelper element spec, or true for a default element spec (defaults to
38934      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38935      */ 
38936     // private
38937     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38938     
38939     // private
38940     hiddenField: false,
38941     
38942     hideMonthPicker : false,
38943     
38944     onRender : function(ct, position)
38945     {
38946         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38947         if (this.useIso) {
38948             this.el.dom.removeAttribute('name'); 
38949             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38950                     'before', true);
38951             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38952             // prevent input submission
38953             this.hiddenName = this.name;
38954         }
38955             
38956             
38957     },
38958     
38959     // private
38960     validateValue : function(value)
38961     {
38962         value = this.formatDate(value);
38963         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38964             return false;
38965         }
38966         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38967              return true;
38968         }
38969         var svalue = value;
38970         value = this.parseDate(value);
38971         if(!value){
38972             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38973             return false;
38974         }
38975         var time = value.getTime();
38976         if(this.minValue && time < this.minValue.getTime()){
38977             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38978             return false;
38979         }
38980         if(this.maxValue && time > this.maxValue.getTime()){
38981             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38982             return false;
38983         }
38984         /*if(this.disabledDays){
38985             var day = value.getDay();
38986             for(var i = 0; i < this.disabledDays.length; i++) {
38987                 if(day === this.disabledDays[i]){
38988                     this.markInvalid(this.disabledDaysText);
38989                     return false;
38990                 }
38991             }
38992         }
38993         */
38994         var fvalue = this.formatDate(value);
38995         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38996             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38997             return false;
38998         }
38999         */
39000         return true;
39001     },
39002
39003     // private
39004     // Provides logic to override the default TriggerField.validateBlur which just returns true
39005     validateBlur : function(){
39006         return !this.menu || !this.menu.isVisible();
39007     },
39008
39009     /**
39010      * Returns the current date value of the date field.
39011      * @return {Date} The date value
39012      */
39013     getValue : function(){
39014         
39015         
39016         
39017         return  this.hiddenField ?
39018                 this.hiddenField.value :
39019                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39020     },
39021
39022     /**
39023      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39024      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39025      * (the default format used is "m/d/y").
39026      * <br />Usage:
39027      * <pre><code>
39028 //All of these calls set the same date value (May 4, 2006)
39029
39030 //Pass a date object:
39031 var dt = new Date('5/4/06');
39032 monthField.setValue(dt);
39033
39034 //Pass a date string (default format):
39035 monthField.setValue('5/4/06');
39036
39037 //Pass a date string (custom format):
39038 monthField.format = 'Y-m-d';
39039 monthField.setValue('2006-5-4');
39040 </code></pre>
39041      * @param {String/Date} date The date or valid date string
39042      */
39043     setValue : function(date){
39044         Roo.log('month setValue' + date);
39045         // can only be first of month..
39046         
39047         var val = this.parseDate(date);
39048         
39049         if (this.hiddenField) {
39050             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39051         }
39052         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39053         this.value = this.parseDate(date);
39054     },
39055
39056     // private
39057     parseDate : function(value){
39058         if(!value || value instanceof Date){
39059             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39060             return value;
39061         }
39062         var v = Date.parseDate(value, this.format);
39063         if (!v && this.useIso) {
39064             v = Date.parseDate(value, 'Y-m-d');
39065         }
39066         if (v) {
39067             // 
39068             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39069         }
39070         
39071         
39072         if(!v && this.altFormats){
39073             if(!this.altFormatsArray){
39074                 this.altFormatsArray = this.altFormats.split("|");
39075             }
39076             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39077                 v = Date.parseDate(value, this.altFormatsArray[i]);
39078             }
39079         }
39080         return v;
39081     },
39082
39083     // private
39084     formatDate : function(date, fmt){
39085         return (!date || !(date instanceof Date)) ?
39086                date : date.dateFormat(fmt || this.format);
39087     },
39088
39089     // private
39090     menuListeners : {
39091         select: function(m, d){
39092             this.setValue(d);
39093             this.fireEvent('select', this, d);
39094         },
39095         show : function(){ // retain focus styling
39096             this.onFocus();
39097         },
39098         hide : function(){
39099             this.focus.defer(10, this);
39100             var ml = this.menuListeners;
39101             this.menu.un("select", ml.select,  this);
39102             this.menu.un("show", ml.show,  this);
39103             this.menu.un("hide", ml.hide,  this);
39104         }
39105     },
39106     // private
39107     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39108     onTriggerClick : function(){
39109         if(this.disabled){
39110             return;
39111         }
39112         if(this.menu == null){
39113             this.menu = new Roo.menu.DateMenu();
39114            
39115         }
39116         
39117         Roo.apply(this.menu.picker,  {
39118             
39119             showClear: this.allowBlank,
39120             minDate : this.minValue,
39121             maxDate : this.maxValue,
39122             disabledDatesRE : this.ddMatch,
39123             disabledDatesText : this.disabledDatesText,
39124             
39125             format : this.useIso ? 'Y-m-d' : this.format,
39126             minText : String.format(this.minText, this.formatDate(this.minValue)),
39127             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39128             
39129         });
39130          this.menu.on(Roo.apply({}, this.menuListeners, {
39131             scope:this
39132         }));
39133        
39134         
39135         var m = this.menu;
39136         var p = m.picker;
39137         
39138         // hide month picker get's called when we called by 'before hide';
39139         
39140         var ignorehide = true;
39141         p.hideMonthPicker  = function(disableAnim){
39142             if (ignorehide) {
39143                 return;
39144             }
39145              if(this.monthPicker){
39146                 Roo.log("hideMonthPicker called");
39147                 if(disableAnim === true){
39148                     this.monthPicker.hide();
39149                 }else{
39150                     this.monthPicker.slideOut('t', {duration:.2});
39151                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39152                     p.fireEvent("select", this, this.value);
39153                     m.hide();
39154                 }
39155             }
39156         }
39157         
39158         Roo.log('picker set value');
39159         Roo.log(this.getValue());
39160         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39161         m.show(this.el, 'tl-bl?');
39162         ignorehide  = false;
39163         // this will trigger hideMonthPicker..
39164         
39165         
39166         // hidden the day picker
39167         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39168         
39169         
39170         
39171       
39172         
39173         p.showMonthPicker.defer(100, p);
39174     
39175         
39176        
39177     },
39178
39179     beforeBlur : function(){
39180         var v = this.parseDate(this.getRawValue());
39181         if(v){
39182             this.setValue(v);
39183         }
39184     }
39185
39186     /** @cfg {Boolean} grow @hide */
39187     /** @cfg {Number} growMin @hide */
39188     /** @cfg {Number} growMax @hide */
39189     /**
39190      * @hide
39191      * @method autoSize
39192      */
39193 });/*
39194  * Based on:
39195  * Ext JS Library 1.1.1
39196  * Copyright(c) 2006-2007, Ext JS, LLC.
39197  *
39198  * Originally Released Under LGPL - original licence link has changed is not relivant.
39199  *
39200  * Fork - LGPL
39201  * <script type="text/javascript">
39202  */
39203  
39204
39205 /**
39206  * @class Roo.form.ComboBox
39207  * @extends Roo.form.TriggerField
39208  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39209  * @constructor
39210  * Create a new ComboBox.
39211  * @param {Object} config Configuration options
39212  */
39213 Roo.form.ComboBox = function(config){
39214     Roo.form.ComboBox.superclass.constructor.call(this, config);
39215     this.addEvents({
39216         /**
39217          * @event expand
39218          * Fires when the dropdown list is expanded
39219              * @param {Roo.form.ComboBox} combo This combo box
39220              */
39221         'expand' : true,
39222         /**
39223          * @event collapse
39224          * Fires when the dropdown list is collapsed
39225              * @param {Roo.form.ComboBox} combo This combo box
39226              */
39227         'collapse' : true,
39228         /**
39229          * @event beforeselect
39230          * Fires before a list item is selected. Return false to cancel the selection.
39231              * @param {Roo.form.ComboBox} combo This combo box
39232              * @param {Roo.data.Record} record The data record returned from the underlying store
39233              * @param {Number} index The index of the selected item in the dropdown list
39234              */
39235         'beforeselect' : true,
39236         /**
39237          * @event select
39238          * Fires when a list item is selected
39239              * @param {Roo.form.ComboBox} combo This combo box
39240              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39241              * @param {Number} index The index of the selected item in the dropdown list
39242              */
39243         'select' : true,
39244         /**
39245          * @event beforequery
39246          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39247          * The event object passed has these properties:
39248              * @param {Roo.form.ComboBox} combo This combo box
39249              * @param {String} query The query
39250              * @param {Boolean} forceAll true to force "all" query
39251              * @param {Boolean} cancel true to cancel the query
39252              * @param {Object} e The query event object
39253              */
39254         'beforequery': true,
39255          /**
39256          * @event add
39257          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39258              * @param {Roo.form.ComboBox} combo This combo box
39259              */
39260         'add' : true,
39261         /**
39262          * @event edit
39263          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39264              * @param {Roo.form.ComboBox} combo This combo box
39265              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39266              */
39267         'edit' : true
39268         
39269         
39270     });
39271     if(this.transform){
39272         this.allowDomMove = false;
39273         var s = Roo.getDom(this.transform);
39274         if(!this.hiddenName){
39275             this.hiddenName = s.name;
39276         }
39277         if(!this.store){
39278             this.mode = 'local';
39279             var d = [], opts = s.options;
39280             for(var i = 0, len = opts.length;i < len; i++){
39281                 var o = opts[i];
39282                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39283                 if(o.selected) {
39284                     this.value = value;
39285                 }
39286                 d.push([value, o.text]);
39287             }
39288             this.store = new Roo.data.SimpleStore({
39289                 'id': 0,
39290                 fields: ['value', 'text'],
39291                 data : d
39292             });
39293             this.valueField = 'value';
39294             this.displayField = 'text';
39295         }
39296         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39297         if(!this.lazyRender){
39298             this.target = true;
39299             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39300             s.parentNode.removeChild(s); // remove it
39301             this.render(this.el.parentNode);
39302         }else{
39303             s.parentNode.removeChild(s); // remove it
39304         }
39305
39306     }
39307     if (this.store) {
39308         this.store = Roo.factory(this.store, Roo.data);
39309     }
39310     
39311     this.selectedIndex = -1;
39312     if(this.mode == 'local'){
39313         if(config.queryDelay === undefined){
39314             this.queryDelay = 10;
39315         }
39316         if(config.minChars === undefined){
39317             this.minChars = 0;
39318         }
39319     }
39320 };
39321
39322 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39323     /**
39324      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39325      */
39326     /**
39327      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39328      * rendering into an Roo.Editor, defaults to false)
39329      */
39330     /**
39331      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39332      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39333      */
39334     /**
39335      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39336      */
39337     /**
39338      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39339      * the dropdown list (defaults to undefined, with no header element)
39340      */
39341
39342      /**
39343      * @cfg {String/Roo.Template} tpl The template to use to render the output
39344      */
39345      
39346     // private
39347     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39348     /**
39349      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39350      */
39351     listWidth: undefined,
39352     /**
39353      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39354      * mode = 'remote' or 'text' if mode = 'local')
39355      */
39356     displayField: undefined,
39357     /**
39358      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39359      * mode = 'remote' or 'value' if mode = 'local'). 
39360      * Note: use of a valueField requires the user make a selection
39361      * in order for a value to be mapped.
39362      */
39363     valueField: undefined,
39364     
39365     
39366     /**
39367      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39368      * field's data value (defaults to the underlying DOM element's name)
39369      */
39370     hiddenName: undefined,
39371     /**
39372      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39373      */
39374     listClass: '',
39375     /**
39376      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39377      */
39378     selectedClass: 'x-combo-selected',
39379     /**
39380      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39381      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39382      * which displays a downward arrow icon).
39383      */
39384     triggerClass : 'x-form-arrow-trigger',
39385     /**
39386      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39387      */
39388     shadow:'sides',
39389     /**
39390      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39391      * anchor positions (defaults to 'tl-bl')
39392      */
39393     listAlign: 'tl-bl?',
39394     /**
39395      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39396      */
39397     maxHeight: 300,
39398     /**
39399      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39400      * query specified by the allQuery config option (defaults to 'query')
39401      */
39402     triggerAction: 'query',
39403     /**
39404      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39405      * (defaults to 4, does not apply if editable = false)
39406      */
39407     minChars : 4,
39408     /**
39409      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39410      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39411      */
39412     typeAhead: false,
39413     /**
39414      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39415      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39416      */
39417     queryDelay: 500,
39418     /**
39419      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39420      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39421      */
39422     pageSize: 0,
39423     /**
39424      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39425      * when editable = true (defaults to false)
39426      */
39427     selectOnFocus:false,
39428     /**
39429      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39430      */
39431     queryParam: 'query',
39432     /**
39433      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39434      * when mode = 'remote' (defaults to 'Loading...')
39435      */
39436     loadingText: 'Loading...',
39437     /**
39438      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39439      */
39440     resizable: false,
39441     /**
39442      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39443      */
39444     handleHeight : 8,
39445     /**
39446      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39447      * traditional select (defaults to true)
39448      */
39449     editable: true,
39450     /**
39451      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39452      */
39453     allQuery: '',
39454     /**
39455      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39456      */
39457     mode: 'remote',
39458     /**
39459      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39460      * listWidth has a higher value)
39461      */
39462     minListWidth : 70,
39463     /**
39464      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39465      * allow the user to set arbitrary text into the field (defaults to false)
39466      */
39467     forceSelection:false,
39468     /**
39469      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39470      * if typeAhead = true (defaults to 250)
39471      */
39472     typeAheadDelay : 250,
39473     /**
39474      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39475      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39476      */
39477     valueNotFoundText : undefined,
39478     /**
39479      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39480      */
39481     blockFocus : false,
39482     
39483     /**
39484      * @cfg {Boolean} disableClear Disable showing of clear button.
39485      */
39486     disableClear : false,
39487     /**
39488      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39489      */
39490     alwaysQuery : false,
39491     
39492     //private
39493     addicon : false,
39494     editicon: false,
39495     
39496     // element that contains real text value.. (when hidden is used..)
39497      
39498     // private
39499     onRender : function(ct, position){
39500         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39501         if(this.hiddenName){
39502             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39503                     'before', true);
39504             this.hiddenField.value =
39505                 this.hiddenValue !== undefined ? this.hiddenValue :
39506                 this.value !== undefined ? this.value : '';
39507
39508             // prevent input submission
39509             this.el.dom.removeAttribute('name');
39510              
39511              
39512         }
39513         if(Roo.isGecko){
39514             this.el.dom.setAttribute('autocomplete', 'off');
39515         }
39516
39517         var cls = 'x-combo-list';
39518
39519         this.list = new Roo.Layer({
39520             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39521         });
39522
39523         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39524         this.list.setWidth(lw);
39525         this.list.swallowEvent('mousewheel');
39526         this.assetHeight = 0;
39527
39528         if(this.title){
39529             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39530             this.assetHeight += this.header.getHeight();
39531         }
39532
39533         this.innerList = this.list.createChild({cls:cls+'-inner'});
39534         this.innerList.on('mouseover', this.onViewOver, this);
39535         this.innerList.on('mousemove', this.onViewMove, this);
39536         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39537         
39538         if(this.allowBlank && !this.pageSize && !this.disableClear){
39539             this.footer = this.list.createChild({cls:cls+'-ft'});
39540             this.pageTb = new Roo.Toolbar(this.footer);
39541            
39542         }
39543         if(this.pageSize){
39544             this.footer = this.list.createChild({cls:cls+'-ft'});
39545             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39546                     {pageSize: this.pageSize});
39547             
39548         }
39549         
39550         if (this.pageTb && this.allowBlank && !this.disableClear) {
39551             var _this = this;
39552             this.pageTb.add(new Roo.Toolbar.Fill(), {
39553                 cls: 'x-btn-icon x-btn-clear',
39554                 text: '&#160;',
39555                 handler: function()
39556                 {
39557                     _this.collapse();
39558                     _this.clearValue();
39559                     _this.onSelect(false, -1);
39560                 }
39561             });
39562         }
39563         if (this.footer) {
39564             this.assetHeight += this.footer.getHeight();
39565         }
39566         
39567
39568         if(!this.tpl){
39569             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39570         }
39571
39572         this.view = new Roo.View(this.innerList, this.tpl, {
39573             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39574         });
39575
39576         this.view.on('click', this.onViewClick, this);
39577
39578         this.store.on('beforeload', this.onBeforeLoad, this);
39579         this.store.on('load', this.onLoad, this);
39580         this.store.on('loadexception', this.onLoadException, this);
39581
39582         if(this.resizable){
39583             this.resizer = new Roo.Resizable(this.list,  {
39584                pinned:true, handles:'se'
39585             });
39586             this.resizer.on('resize', function(r, w, h){
39587                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39588                 this.listWidth = w;
39589                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39590                 this.restrictHeight();
39591             }, this);
39592             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39593         }
39594         if(!this.editable){
39595             this.editable = true;
39596             this.setEditable(false);
39597         }  
39598         
39599         
39600         if (typeof(this.events.add.listeners) != 'undefined') {
39601             
39602             this.addicon = this.wrap.createChild(
39603                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39604        
39605             this.addicon.on('click', function(e) {
39606                 this.fireEvent('add', this);
39607             }, this);
39608         }
39609         if (typeof(this.events.edit.listeners) != 'undefined') {
39610             
39611             this.editicon = this.wrap.createChild(
39612                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39613             if (this.addicon) {
39614                 this.editicon.setStyle('margin-left', '40px');
39615             }
39616             this.editicon.on('click', function(e) {
39617                 
39618                 // we fire even  if inothing is selected..
39619                 this.fireEvent('edit', this, this.lastData );
39620                 
39621             }, this);
39622         }
39623         
39624         
39625         
39626     },
39627
39628     // private
39629     initEvents : function(){
39630         Roo.form.ComboBox.superclass.initEvents.call(this);
39631
39632         this.keyNav = new Roo.KeyNav(this.el, {
39633             "up" : function(e){
39634                 this.inKeyMode = true;
39635                 this.selectPrev();
39636             },
39637
39638             "down" : function(e){
39639                 if(!this.isExpanded()){
39640                     this.onTriggerClick();
39641                 }else{
39642                     this.inKeyMode = true;
39643                     this.selectNext();
39644                 }
39645             },
39646
39647             "enter" : function(e){
39648                 this.onViewClick();
39649                 //return true;
39650             },
39651
39652             "esc" : function(e){
39653                 this.collapse();
39654             },
39655
39656             "tab" : function(e){
39657                 this.onViewClick(false);
39658                 this.fireEvent("specialkey", this, e);
39659                 return true;
39660             },
39661
39662             scope : this,
39663
39664             doRelay : function(foo, bar, hname){
39665                 if(hname == 'down' || this.scope.isExpanded()){
39666                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39667                 }
39668                 return true;
39669             },
39670
39671             forceKeyDown: true
39672         });
39673         this.queryDelay = Math.max(this.queryDelay || 10,
39674                 this.mode == 'local' ? 10 : 250);
39675         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39676         if(this.typeAhead){
39677             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39678         }
39679         if(this.editable !== false){
39680             this.el.on("keyup", this.onKeyUp, this);
39681         }
39682         if(this.forceSelection){
39683             this.on('blur', this.doForce, this);
39684         }
39685     },
39686
39687     onDestroy : function(){
39688         if(this.view){
39689             this.view.setStore(null);
39690             this.view.el.removeAllListeners();
39691             this.view.el.remove();
39692             this.view.purgeListeners();
39693         }
39694         if(this.list){
39695             this.list.destroy();
39696         }
39697         if(this.store){
39698             this.store.un('beforeload', this.onBeforeLoad, this);
39699             this.store.un('load', this.onLoad, this);
39700             this.store.un('loadexception', this.onLoadException, this);
39701         }
39702         Roo.form.ComboBox.superclass.onDestroy.call(this);
39703     },
39704
39705     // private
39706     fireKey : function(e){
39707         if(e.isNavKeyPress() && !this.list.isVisible()){
39708             this.fireEvent("specialkey", this, e);
39709         }
39710     },
39711
39712     // private
39713     onResize: function(w, h){
39714         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39715         
39716         if(typeof w != 'number'){
39717             // we do not handle it!?!?
39718             return;
39719         }
39720         var tw = this.trigger.getWidth();
39721         tw += this.addicon ? this.addicon.getWidth() : 0;
39722         tw += this.editicon ? this.editicon.getWidth() : 0;
39723         var x = w - tw;
39724         this.el.setWidth( this.adjustWidth('input', x));
39725             
39726         this.trigger.setStyle('left', x+'px');
39727         
39728         if(this.list && this.listWidth === undefined){
39729             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39730             this.list.setWidth(lw);
39731             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39732         }
39733         
39734     
39735         
39736     },
39737
39738     /**
39739      * Allow or prevent the user from directly editing the field text.  If false is passed,
39740      * the user will only be able to select from the items defined in the dropdown list.  This method
39741      * is the runtime equivalent of setting the 'editable' config option at config time.
39742      * @param {Boolean} value True to allow the user to directly edit the field text
39743      */
39744     setEditable : function(value){
39745         if(value == this.editable){
39746             return;
39747         }
39748         this.editable = value;
39749         if(!value){
39750             this.el.dom.setAttribute('readOnly', true);
39751             this.el.on('mousedown', this.onTriggerClick,  this);
39752             this.el.addClass('x-combo-noedit');
39753         }else{
39754             this.el.dom.setAttribute('readOnly', false);
39755             this.el.un('mousedown', this.onTriggerClick,  this);
39756             this.el.removeClass('x-combo-noedit');
39757         }
39758     },
39759
39760     // private
39761     onBeforeLoad : function(){
39762         if(!this.hasFocus){
39763             return;
39764         }
39765         this.innerList.update(this.loadingText ?
39766                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39767         this.restrictHeight();
39768         this.selectedIndex = -1;
39769     },
39770
39771     // private
39772     onLoad : function(){
39773         if(!this.hasFocus){
39774             return;
39775         }
39776         if(this.store.getCount() > 0){
39777             this.expand();
39778             this.restrictHeight();
39779             if(this.lastQuery == this.allQuery){
39780                 if(this.editable){
39781                     this.el.dom.select();
39782                 }
39783                 if(!this.selectByValue(this.value, true)){
39784                     this.select(0, true);
39785                 }
39786             }else{
39787                 this.selectNext();
39788                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39789                     this.taTask.delay(this.typeAheadDelay);
39790                 }
39791             }
39792         }else{
39793             this.onEmptyResults();
39794         }
39795         //this.el.focus();
39796     },
39797     // private
39798     onLoadException : function()
39799     {
39800         this.collapse();
39801         Roo.log(this.store.reader.jsonData);
39802         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39803             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39804         }
39805         
39806         
39807     },
39808     // private
39809     onTypeAhead : function(){
39810         if(this.store.getCount() > 0){
39811             var r = this.store.getAt(0);
39812             var newValue = r.data[this.displayField];
39813             var len = newValue.length;
39814             var selStart = this.getRawValue().length;
39815             if(selStart != len){
39816                 this.setRawValue(newValue);
39817                 this.selectText(selStart, newValue.length);
39818             }
39819         }
39820     },
39821
39822     // private
39823     onSelect : function(record, index){
39824         if(this.fireEvent('beforeselect', this, record, index) !== false){
39825             this.setFromData(index > -1 ? record.data : false);
39826             this.collapse();
39827             this.fireEvent('select', this, record, index);
39828         }
39829     },
39830
39831     /**
39832      * Returns the currently selected field value or empty string if no value is set.
39833      * @return {String} value The selected value
39834      */
39835     getValue : function(){
39836         if(this.valueField){
39837             return typeof this.value != 'undefined' ? this.value : '';
39838         }
39839         return Roo.form.ComboBox.superclass.getValue.call(this);
39840     },
39841
39842     /**
39843      * Clears any text/value currently set in the field
39844      */
39845     clearValue : function(){
39846         if(this.hiddenField){
39847             this.hiddenField.value = '';
39848         }
39849         this.value = '';
39850         this.setRawValue('');
39851         this.lastSelectionText = '';
39852         
39853     },
39854
39855     /**
39856      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39857      * will be displayed in the field.  If the value does not match the data value of an existing item,
39858      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39859      * Otherwise the field will be blank (although the value will still be set).
39860      * @param {String} value The value to match
39861      */
39862     setValue : function(v){
39863         var text = v;
39864         if(this.valueField){
39865             var r = this.findRecord(this.valueField, v);
39866             if(r){
39867                 text = r.data[this.displayField];
39868             }else if(this.valueNotFoundText !== undefined){
39869                 text = this.valueNotFoundText;
39870             }
39871         }
39872         this.lastSelectionText = text;
39873         if(this.hiddenField){
39874             this.hiddenField.value = v;
39875         }
39876         Roo.form.ComboBox.superclass.setValue.call(this, text);
39877         this.value = v;
39878     },
39879     /**
39880      * @property {Object} the last set data for the element
39881      */
39882     
39883     lastData : false,
39884     /**
39885      * Sets the value of the field based on a object which is related to the record format for the store.
39886      * @param {Object} value the value to set as. or false on reset?
39887      */
39888     setFromData : function(o){
39889         var dv = ''; // display value
39890         var vv = ''; // value value..
39891         this.lastData = o;
39892         if (this.displayField) {
39893             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39894         } else {
39895             // this is an error condition!!!
39896             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39897         }
39898         
39899         if(this.valueField){
39900             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39901         }
39902         if(this.hiddenField){
39903             this.hiddenField.value = vv;
39904             
39905             this.lastSelectionText = dv;
39906             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39907             this.value = vv;
39908             return;
39909         }
39910         // no hidden field.. - we store the value in 'value', but still display
39911         // display field!!!!
39912         this.lastSelectionText = dv;
39913         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39914         this.value = vv;
39915         
39916         
39917     },
39918     // private
39919     reset : function(){
39920         // overridden so that last data is reset..
39921         this.setValue(this.resetValue);
39922         this.clearInvalid();
39923         this.lastData = false;
39924         if (this.view) {
39925             this.view.clearSelections();
39926         }
39927     },
39928     // private
39929     findRecord : function(prop, value){
39930         var record;
39931         if(this.store.getCount() > 0){
39932             this.store.each(function(r){
39933                 if(r.data[prop] == value){
39934                     record = r;
39935                     return false;
39936                 }
39937                 return true;
39938             });
39939         }
39940         return record;
39941     },
39942     
39943     getName: function()
39944     {
39945         // returns hidden if it's set..
39946         if (!this.rendered) {return ''};
39947         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39948         
39949     },
39950     // private
39951     onViewMove : function(e, t){
39952         this.inKeyMode = false;
39953     },
39954
39955     // private
39956     onViewOver : function(e, t){
39957         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39958             return;
39959         }
39960         var item = this.view.findItemFromChild(t);
39961         if(item){
39962             var index = this.view.indexOf(item);
39963             this.select(index, false);
39964         }
39965     },
39966
39967     // private
39968     onViewClick : function(doFocus)
39969     {
39970         var index = this.view.getSelectedIndexes()[0];
39971         var r = this.store.getAt(index);
39972         if(r){
39973             this.onSelect(r, index);
39974         }
39975         if(doFocus !== false && !this.blockFocus){
39976             this.el.focus();
39977         }
39978     },
39979
39980     // private
39981     restrictHeight : function(){
39982         this.innerList.dom.style.height = '';
39983         var inner = this.innerList.dom;
39984         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39985         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39986         this.list.beginUpdate();
39987         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39988         this.list.alignTo(this.el, this.listAlign);
39989         this.list.endUpdate();
39990     },
39991
39992     // private
39993     onEmptyResults : function(){
39994         this.collapse();
39995     },
39996
39997     /**
39998      * Returns true if the dropdown list is expanded, else false.
39999      */
40000     isExpanded : function(){
40001         return this.list.isVisible();
40002     },
40003
40004     /**
40005      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40006      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40007      * @param {String} value The data value of the item to select
40008      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40009      * selected item if it is not currently in view (defaults to true)
40010      * @return {Boolean} True if the value matched an item in the list, else false
40011      */
40012     selectByValue : function(v, scrollIntoView){
40013         if(v !== undefined && v !== null){
40014             var r = this.findRecord(this.valueField || this.displayField, v);
40015             if(r){
40016                 this.select(this.store.indexOf(r), scrollIntoView);
40017                 return true;
40018             }
40019         }
40020         return false;
40021     },
40022
40023     /**
40024      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40025      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40026      * @param {Number} index The zero-based index of the list item to select
40027      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40028      * selected item if it is not currently in view (defaults to true)
40029      */
40030     select : function(index, scrollIntoView){
40031         this.selectedIndex = index;
40032         this.view.select(index);
40033         if(scrollIntoView !== false){
40034             var el = this.view.getNode(index);
40035             if(el){
40036                 this.innerList.scrollChildIntoView(el, false);
40037             }
40038         }
40039     },
40040
40041     // private
40042     selectNext : function(){
40043         var ct = this.store.getCount();
40044         if(ct > 0){
40045             if(this.selectedIndex == -1){
40046                 this.select(0);
40047             }else if(this.selectedIndex < ct-1){
40048                 this.select(this.selectedIndex+1);
40049             }
40050         }
40051     },
40052
40053     // private
40054     selectPrev : function(){
40055         var ct = this.store.getCount();
40056         if(ct > 0){
40057             if(this.selectedIndex == -1){
40058                 this.select(0);
40059             }else if(this.selectedIndex != 0){
40060                 this.select(this.selectedIndex-1);
40061             }
40062         }
40063     },
40064
40065     // private
40066     onKeyUp : function(e){
40067         if(this.editable !== false && !e.isSpecialKey()){
40068             this.lastKey = e.getKey();
40069             this.dqTask.delay(this.queryDelay);
40070         }
40071     },
40072
40073     // private
40074     validateBlur : function(){
40075         return !this.list || !this.list.isVisible();   
40076     },
40077
40078     // private
40079     initQuery : function(){
40080         this.doQuery(this.getRawValue());
40081     },
40082
40083     // private
40084     doForce : function(){
40085         if(this.el.dom.value.length > 0){
40086             this.el.dom.value =
40087                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40088              
40089         }
40090     },
40091
40092     /**
40093      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40094      * query allowing the query action to be canceled if needed.
40095      * @param {String} query The SQL query to execute
40096      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40097      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40098      * saved in the current store (defaults to false)
40099      */
40100     doQuery : function(q, forceAll){
40101         if(q === undefined || q === null){
40102             q = '';
40103         }
40104         var qe = {
40105             query: q,
40106             forceAll: forceAll,
40107             combo: this,
40108             cancel:false
40109         };
40110         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40111             return false;
40112         }
40113         q = qe.query;
40114         forceAll = qe.forceAll;
40115         if(forceAll === true || (q.length >= this.minChars)){
40116             if(this.lastQuery != q || this.alwaysQuery){
40117                 this.lastQuery = q;
40118                 if(this.mode == 'local'){
40119                     this.selectedIndex = -1;
40120                     if(forceAll){
40121                         this.store.clearFilter();
40122                     }else{
40123                         this.store.filter(this.displayField, q);
40124                     }
40125                     this.onLoad();
40126                 }else{
40127                     this.store.baseParams[this.queryParam] = q;
40128                     this.store.load({
40129                         params: this.getParams(q)
40130                     });
40131                     this.expand();
40132                 }
40133             }else{
40134                 this.selectedIndex = -1;
40135                 this.onLoad();   
40136             }
40137         }
40138     },
40139
40140     // private
40141     getParams : function(q){
40142         var p = {};
40143         //p[this.queryParam] = q;
40144         if(this.pageSize){
40145             p.start = 0;
40146             p.limit = this.pageSize;
40147         }
40148         return p;
40149     },
40150
40151     /**
40152      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40153      */
40154     collapse : function(){
40155         if(!this.isExpanded()){
40156             return;
40157         }
40158         this.list.hide();
40159         Roo.get(document).un('mousedown', this.collapseIf, this);
40160         Roo.get(document).un('mousewheel', this.collapseIf, this);
40161         if (!this.editable) {
40162             Roo.get(document).un('keydown', this.listKeyPress, this);
40163         }
40164         this.fireEvent('collapse', this);
40165     },
40166
40167     // private
40168     collapseIf : function(e){
40169         if(!e.within(this.wrap) && !e.within(this.list)){
40170             this.collapse();
40171         }
40172     },
40173
40174     /**
40175      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40176      */
40177     expand : function(){
40178         if(this.isExpanded() || !this.hasFocus){
40179             return;
40180         }
40181         this.list.alignTo(this.el, this.listAlign);
40182         this.list.show();
40183         Roo.get(document).on('mousedown', this.collapseIf, this);
40184         Roo.get(document).on('mousewheel', this.collapseIf, this);
40185         if (!this.editable) {
40186             Roo.get(document).on('keydown', this.listKeyPress, this);
40187         }
40188         
40189         this.fireEvent('expand', this);
40190     },
40191
40192     // private
40193     // Implements the default empty TriggerField.onTriggerClick function
40194     onTriggerClick : function(){
40195         if(this.disabled){
40196             return;
40197         }
40198         if(this.isExpanded()){
40199             this.collapse();
40200             if (!this.blockFocus) {
40201                 this.el.focus();
40202             }
40203             
40204         }else {
40205             this.hasFocus = true;
40206             if(this.triggerAction == 'all') {
40207                 this.doQuery(this.allQuery, true);
40208             } else {
40209                 this.doQuery(this.getRawValue());
40210             }
40211             if (!this.blockFocus) {
40212                 this.el.focus();
40213             }
40214         }
40215     },
40216     listKeyPress : function(e)
40217     {
40218         //Roo.log('listkeypress');
40219         // scroll to first matching element based on key pres..
40220         if (e.isSpecialKey()) {
40221             return false;
40222         }
40223         var k = String.fromCharCode(e.getKey()).toUpperCase();
40224         //Roo.log(k);
40225         var match  = false;
40226         var csel = this.view.getSelectedNodes();
40227         var cselitem = false;
40228         if (csel.length) {
40229             var ix = this.view.indexOf(csel[0]);
40230             cselitem  = this.store.getAt(ix);
40231             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40232                 cselitem = false;
40233             }
40234             
40235         }
40236         
40237         this.store.each(function(v) { 
40238             if (cselitem) {
40239                 // start at existing selection.
40240                 if (cselitem.id == v.id) {
40241                     cselitem = false;
40242                 }
40243                 return;
40244             }
40245                 
40246             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40247                 match = this.store.indexOf(v);
40248                 return false;
40249             }
40250         }, this);
40251         
40252         if (match === false) {
40253             return true; // no more action?
40254         }
40255         // scroll to?
40256         this.view.select(match);
40257         var sn = Roo.get(this.view.getSelectedNodes()[0])
40258         sn.scrollIntoView(sn.dom.parentNode, false);
40259     }
40260
40261     /** 
40262     * @cfg {Boolean} grow 
40263     * @hide 
40264     */
40265     /** 
40266     * @cfg {Number} growMin 
40267     * @hide 
40268     */
40269     /** 
40270     * @cfg {Number} growMax 
40271     * @hide 
40272     */
40273     /**
40274      * @hide
40275      * @method autoSize
40276      */
40277 });/*
40278  * Copyright(c) 2010-2012, Roo J Solutions Limited
40279  *
40280  * Licence LGPL
40281  *
40282  */
40283
40284 /**
40285  * @class Roo.form.ComboBoxArray
40286  * @extends Roo.form.TextField
40287  * A facebook style adder... for lists of email / people / countries  etc...
40288  * pick multiple items from a combo box, and shows each one.
40289  *
40290  *  Fred [x]  Brian [x]  [Pick another |v]
40291  *
40292  *
40293  *  For this to work: it needs various extra information
40294  *    - normal combo problay has
40295  *      name, hiddenName
40296  *    + displayField, valueField
40297  *
40298  *    For our purpose...
40299  *
40300  *
40301  *   If we change from 'extends' to wrapping...
40302  *   
40303  *  
40304  *
40305  
40306  
40307  * @constructor
40308  * Create a new ComboBoxArray.
40309  * @param {Object} config Configuration options
40310  */
40311  
40312
40313 Roo.form.ComboBoxArray = function(config)
40314 {
40315     this.addEvents({
40316         /**
40317          * @event remove
40318          * Fires when remove the value from the list
40319              * @param {Roo.form.ComboBoxArray} _self This combo box array
40320              * @param {Roo.form.ComboBoxArray.Item} item removed item
40321              */
40322         'remove' : true
40323         
40324         
40325     });
40326     
40327     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40328     
40329     this.items = new Roo.util.MixedCollection(false);
40330     
40331     // construct the child combo...
40332     
40333     
40334     
40335     
40336    
40337     
40338 }
40339
40340  
40341 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40342
40343     /**
40344      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40345      */
40346     
40347     lastData : false,
40348     
40349     // behavies liek a hiddne field
40350     inputType:      'hidden',
40351     /**
40352      * @cfg {Number} width The width of the box that displays the selected element
40353      */ 
40354     width:          300,
40355
40356     
40357     
40358     /**
40359      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40360      */
40361     name : false,
40362     /**
40363      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40364      */
40365     hiddenName : false,
40366     
40367     
40368     // private the array of items that are displayed..
40369     items  : false,
40370     // private - the hidden field el.
40371     hiddenEl : false,
40372     // private - the filed el..
40373     el : false,
40374     
40375     //validateValue : function() { return true; }, // all values are ok!
40376     //onAddClick: function() { },
40377     
40378     onRender : function(ct, position) 
40379     {
40380         
40381         // create the standard hidden element
40382         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40383         
40384         
40385         // give fake names to child combo;
40386         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40387         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40388         
40389         this.combo = Roo.factory(this.combo, Roo.form);
40390         this.combo.onRender(ct, position);
40391         if (typeof(this.combo.width) != 'undefined') {
40392             this.combo.onResize(this.combo.width,0);
40393         }
40394         
40395         this.combo.initEvents();
40396         
40397         // assigned so form know we need to do this..
40398         this.store          = this.combo.store;
40399         this.valueField     = this.combo.valueField;
40400         this.displayField   = this.combo.displayField ;
40401         
40402         
40403         this.combo.wrap.addClass('x-cbarray-grp');
40404         
40405         var cbwrap = this.combo.wrap.createChild(
40406             {tag: 'div', cls: 'x-cbarray-cb'},
40407             this.combo.el.dom
40408         );
40409         
40410              
40411         this.hiddenEl = this.combo.wrap.createChild({
40412             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40413         });
40414         this.el = this.combo.wrap.createChild({
40415             tag: 'input',  type:'hidden' , name: this.name, value : ''
40416         });
40417          //   this.el.dom.removeAttribute("name");
40418         
40419         
40420         this.outerWrap = this.combo.wrap;
40421         this.wrap = cbwrap;
40422         
40423         this.outerWrap.setWidth(this.width);
40424         this.outerWrap.dom.removeChild(this.el.dom);
40425         
40426         this.wrap.dom.appendChild(this.el.dom);
40427         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40428         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40429         
40430         this.combo.trigger.setStyle('position','relative');
40431         this.combo.trigger.setStyle('left', '0px');
40432         this.combo.trigger.setStyle('top', '2px');
40433         
40434         this.combo.el.setStyle('vertical-align', 'text-bottom');
40435         
40436         //this.trigger.setStyle('vertical-align', 'top');
40437         
40438         // this should use the code from combo really... on('add' ....)
40439         if (this.adder) {
40440             
40441         
40442             this.adder = this.outerWrap.createChild(
40443                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40444             var _t = this;
40445             this.adder.on('click', function(e) {
40446                 _t.fireEvent('adderclick', this, e);
40447             }, _t);
40448         }
40449         //var _t = this;
40450         //this.adder.on('click', this.onAddClick, _t);
40451         
40452         
40453         this.combo.on('select', function(cb, rec, ix) {
40454             this.addItem(rec.data);
40455             
40456             cb.setValue('');
40457             cb.el.dom.value = '';
40458             //cb.lastData = rec.data;
40459             // add to list
40460             
40461         }, this);
40462         
40463         
40464     },
40465     
40466     
40467     getName: function()
40468     {
40469         // returns hidden if it's set..
40470         if (!this.rendered) {return ''};
40471         return  this.hiddenName ? this.hiddenName : this.name;
40472         
40473     },
40474     
40475     
40476     onResize: function(w, h){
40477         
40478         return;
40479         // not sure if this is needed..
40480         //this.combo.onResize(w,h);
40481         
40482         if(typeof w != 'number'){
40483             // we do not handle it!?!?
40484             return;
40485         }
40486         var tw = this.combo.trigger.getWidth();
40487         tw += this.addicon ? this.addicon.getWidth() : 0;
40488         tw += this.editicon ? this.editicon.getWidth() : 0;
40489         var x = w - tw;
40490         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40491             
40492         this.combo.trigger.setStyle('left', '0px');
40493         
40494         if(this.list && this.listWidth === undefined){
40495             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40496             this.list.setWidth(lw);
40497             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40498         }
40499         
40500     
40501         
40502     },
40503     
40504     addItem: function(rec)
40505     {
40506         var valueField = this.combo.valueField;
40507         var displayField = this.combo.displayField;
40508         if (this.items.indexOfKey(rec[valueField]) > -1) {
40509             //console.log("GOT " + rec.data.id);
40510             return;
40511         }
40512         
40513         var x = new Roo.form.ComboBoxArray.Item({
40514             //id : rec[this.idField],
40515             data : rec,
40516             displayField : displayField ,
40517             tipField : displayField ,
40518             cb : this
40519         });
40520         // use the 
40521         this.items.add(rec[valueField],x);
40522         // add it before the element..
40523         this.updateHiddenEl();
40524         x.render(this.outerWrap, this.wrap.dom);
40525         // add the image handler..
40526     },
40527     
40528     updateHiddenEl : function()
40529     {
40530         this.validate();
40531         if (!this.hiddenEl) {
40532             return;
40533         }
40534         var ar = [];
40535         var idField = this.combo.valueField;
40536         
40537         this.items.each(function(f) {
40538             ar.push(f.data[idField]);
40539            
40540         });
40541         this.hiddenEl.dom.value = ar.join(',');
40542         this.validate();
40543     },
40544     
40545     reset : function()
40546     {
40547         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40548         this.items.each(function(f) {
40549            f.remove(); 
40550         });
40551         this.el.dom.value = '';
40552         if (this.hiddenEl) {
40553             this.hiddenEl.dom.value = '';
40554         }
40555         
40556     },
40557     getValue: function()
40558     {
40559         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40560     },
40561     setValue: function(v) // not a valid action - must use addItems..
40562     {
40563          
40564         this.reset();
40565         
40566         
40567         
40568         if (this.store.isLocal && (typeof(v) == 'string')) {
40569             // then we can use the store to find the values..
40570             // comma seperated at present.. this needs to allow JSON based encoding..
40571             this.hiddenEl.value  = v;
40572             var v_ar = [];
40573             Roo.each(v.split(','), function(k) {
40574                 Roo.log("CHECK " + this.valueField + ',' + k);
40575                 var li = this.store.query(this.valueField, k);
40576                 if (!li.length) {
40577                     return;
40578                 }
40579                 var add = {};
40580                 add[this.valueField] = k;
40581                 add[this.displayField] = li.item(0).data[this.displayField];
40582                 
40583                 this.addItem(add);
40584             }, this) 
40585              
40586         }
40587         if (typeof(v) == 'object' ) {
40588             // then let's assume it's an array of objects..
40589             Roo.each(v, function(l) {
40590                 this.addItem(l);
40591             }, this);
40592              
40593         }
40594         
40595         
40596     },
40597     setFromData: function(v)
40598     {
40599         // this recieves an object, if setValues is called.
40600         this.reset();
40601         this.el.dom.value = v[this.displayField];
40602         this.hiddenEl.dom.value = v[this.valueField];
40603         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40604             return;
40605         }
40606         var kv = v[this.valueField];
40607         var dv = v[this.displayField];
40608         kv = typeof(kv) != 'string' ? '' : kv;
40609         dv = typeof(dv) != 'string' ? '' : dv;
40610         
40611         
40612         var keys = kv.split(',');
40613         var display = dv.split(',');
40614         for (var i = 0 ; i < keys.length; i++) {
40615             
40616             add = {};
40617             add[this.valueField] = keys[i];
40618             add[this.displayField] = display[i];
40619             this.addItem(add);
40620         }
40621       
40622         
40623     },
40624     
40625     /**
40626      * Validates the combox array value
40627      * @return {Boolean} True if the value is valid, else false
40628      */
40629     validate : function(){
40630         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40631             this.clearInvalid();
40632             return true;
40633         }
40634         return false;
40635     },
40636     
40637     validateValue : function(value){
40638         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40639         
40640     },
40641     
40642     /*@
40643      * overide
40644      * 
40645      */
40646     isDirty : function() {
40647         if(this.disabled) {
40648             return false;
40649         }
40650         
40651         try {
40652             var d = Roo.decode(String(this.originalValue));
40653         } catch (e) {
40654             return String(this.getValue()) !== String(this.originalValue);
40655         }
40656         
40657         var originalValue = [];
40658         
40659         for (var i = 0; i < d.length; i++){
40660             originalValue.push(d[i][this.valueField]);
40661         }
40662         
40663         return String(this.getValue()) !== String(originalValue.join(','));
40664         
40665     }
40666     
40667 });
40668
40669
40670
40671 /**
40672  * @class Roo.form.ComboBoxArray.Item
40673  * @extends Roo.BoxComponent
40674  * A selected item in the list
40675  *  Fred [x]  Brian [x]  [Pick another |v]
40676  * 
40677  * @constructor
40678  * Create a new item.
40679  * @param {Object} config Configuration options
40680  */
40681  
40682 Roo.form.ComboBoxArray.Item = function(config) {
40683     config.id = Roo.id();
40684     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40685 }
40686
40687 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40688     data : {},
40689     cb: false,
40690     displayField : false,
40691     tipField : false,
40692     
40693     
40694     defaultAutoCreate : {
40695         tag: 'div',
40696         cls: 'x-cbarray-item',
40697         cn : [ 
40698             { tag: 'div' },
40699             {
40700                 tag: 'img',
40701                 width:16,
40702                 height : 16,
40703                 src : Roo.BLANK_IMAGE_URL ,
40704                 align: 'center'
40705             }
40706         ]
40707         
40708     },
40709     
40710  
40711     onRender : function(ct, position)
40712     {
40713         Roo.form.Field.superclass.onRender.call(this, ct, position);
40714         
40715         if(!this.el){
40716             var cfg = this.getAutoCreate();
40717             this.el = ct.createChild(cfg, position);
40718         }
40719         
40720         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40721         
40722         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40723             this.cb.renderer(this.data) :
40724             String.format('{0}',this.data[this.displayField]);
40725         
40726             
40727         this.el.child('div').dom.setAttribute('qtip',
40728                         String.format('{0}',this.data[this.tipField])
40729         );
40730         
40731         this.el.child('img').on('click', this.remove, this);
40732         
40733     },
40734    
40735     remove : function()
40736     {
40737         if(this.cb.disabled){
40738             return;
40739         }
40740         this.cb.items.remove(this);
40741         this.el.child('img').un('click', this.remove, this);
40742         this.el.remove();
40743         this.cb.updateHiddenEl();
40744         
40745         this.cb.fireEvent('remove', this.cb, this);
40746     }
40747 });/*
40748  * Based on:
40749  * Ext JS Library 1.1.1
40750  * Copyright(c) 2006-2007, Ext JS, LLC.
40751  *
40752  * Originally Released Under LGPL - original licence link has changed is not relivant.
40753  *
40754  * Fork - LGPL
40755  * <script type="text/javascript">
40756  */
40757 /**
40758  * @class Roo.form.Checkbox
40759  * @extends Roo.form.Field
40760  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40761  * @constructor
40762  * Creates a new Checkbox
40763  * @param {Object} config Configuration options
40764  */
40765 Roo.form.Checkbox = function(config){
40766     Roo.form.Checkbox.superclass.constructor.call(this, config);
40767     this.addEvents({
40768         /**
40769          * @event check
40770          * Fires when the checkbox is checked or unchecked.
40771              * @param {Roo.form.Checkbox} this This checkbox
40772              * @param {Boolean} checked The new checked value
40773              */
40774         check : true
40775     });
40776 };
40777
40778 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40779     /**
40780      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40781      */
40782     focusClass : undefined,
40783     /**
40784      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40785      */
40786     fieldClass: "x-form-field",
40787     /**
40788      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40789      */
40790     checked: false,
40791     /**
40792      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40793      * {tag: "input", type: "checkbox", autocomplete: "off"})
40794      */
40795     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40796     /**
40797      * @cfg {String} boxLabel The text that appears beside the checkbox
40798      */
40799     boxLabel : "",
40800     /**
40801      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40802      */  
40803     inputValue : '1',
40804     /**
40805      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40806      */
40807      valueOff: '0', // value when not checked..
40808
40809     actionMode : 'viewEl', 
40810     //
40811     // private
40812     itemCls : 'x-menu-check-item x-form-item',
40813     groupClass : 'x-menu-group-item',
40814     inputType : 'hidden',
40815     
40816     
40817     inSetChecked: false, // check that we are not calling self...
40818     
40819     inputElement: false, // real input element?
40820     basedOn: false, // ????
40821     
40822     isFormField: true, // not sure where this is needed!!!!
40823
40824     onResize : function(){
40825         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40826         if(!this.boxLabel){
40827             this.el.alignTo(this.wrap, 'c-c');
40828         }
40829     },
40830
40831     initEvents : function(){
40832         Roo.form.Checkbox.superclass.initEvents.call(this);
40833         this.el.on("click", this.onClick,  this);
40834         this.el.on("change", this.onClick,  this);
40835     },
40836
40837
40838     getResizeEl : function(){
40839         return this.wrap;
40840     },
40841
40842     getPositionEl : function(){
40843         return this.wrap;
40844     },
40845
40846     // private
40847     onRender : function(ct, position){
40848         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40849         /*
40850         if(this.inputValue !== undefined){
40851             this.el.dom.value = this.inputValue;
40852         }
40853         */
40854         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40855         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40856         var viewEl = this.wrap.createChild({ 
40857             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40858         this.viewEl = viewEl;   
40859         this.wrap.on('click', this.onClick,  this); 
40860         
40861         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40862         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40863         
40864         
40865         
40866         if(this.boxLabel){
40867             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40868         //    viewEl.on('click', this.onClick,  this); 
40869         }
40870         //if(this.checked){
40871             this.setChecked(this.checked);
40872         //}else{
40873             //this.checked = this.el.dom;
40874         //}
40875
40876     },
40877
40878     // private
40879     initValue : Roo.emptyFn,
40880
40881     /**
40882      * Returns the checked state of the checkbox.
40883      * @return {Boolean} True if checked, else false
40884      */
40885     getValue : function(){
40886         if(this.el){
40887             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40888         }
40889         return this.valueOff;
40890         
40891     },
40892
40893         // private
40894     onClick : function(){ 
40895         if (this.disabled) {
40896             return;
40897         }
40898         this.setChecked(!this.checked);
40899
40900         //if(this.el.dom.checked != this.checked){
40901         //    this.setValue(this.el.dom.checked);
40902        // }
40903     },
40904
40905     /**
40906      * Sets the checked state of the checkbox.
40907      * On is always based on a string comparison between inputValue and the param.
40908      * @param {Boolean/String} value - the value to set 
40909      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40910      */
40911     setValue : function(v,suppressEvent){
40912         
40913         
40914         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40915         //if(this.el && this.el.dom){
40916         //    this.el.dom.checked = this.checked;
40917         //    this.el.dom.defaultChecked = this.checked;
40918         //}
40919         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40920         //this.fireEvent("check", this, this.checked);
40921     },
40922     // private..
40923     setChecked : function(state,suppressEvent)
40924     {
40925         if (this.inSetChecked) {
40926             this.checked = state;
40927             return;
40928         }
40929         
40930     
40931         if(this.wrap){
40932             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40933         }
40934         this.checked = state;
40935         if(suppressEvent !== true){
40936             this.fireEvent('check', this, state);
40937         }
40938         this.inSetChecked = true;
40939         this.el.dom.value = state ? this.inputValue : this.valueOff;
40940         this.inSetChecked = false;
40941         
40942     },
40943     // handle setting of hidden value by some other method!!?!?
40944     setFromHidden: function()
40945     {
40946         if(!this.el){
40947             return;
40948         }
40949         //console.log("SET FROM HIDDEN");
40950         //alert('setFrom hidden');
40951         this.setValue(this.el.dom.value);
40952     },
40953     
40954     onDestroy : function()
40955     {
40956         if(this.viewEl){
40957             Roo.get(this.viewEl).remove();
40958         }
40959          
40960         Roo.form.Checkbox.superclass.onDestroy.call(this);
40961     }
40962
40963 });/*
40964  * Based on:
40965  * Ext JS Library 1.1.1
40966  * Copyright(c) 2006-2007, Ext JS, LLC.
40967  *
40968  * Originally Released Under LGPL - original licence link has changed is not relivant.
40969  *
40970  * Fork - LGPL
40971  * <script type="text/javascript">
40972  */
40973  
40974 /**
40975  * @class Roo.form.Radio
40976  * @extends Roo.form.Checkbox
40977  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40978  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40979  * @constructor
40980  * Creates a new Radio
40981  * @param {Object} config Configuration options
40982  */
40983 Roo.form.Radio = function(){
40984     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40985 };
40986 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40987     inputType: 'radio',
40988
40989     /**
40990      * If this radio is part of a group, it will return the selected value
40991      * @return {String}
40992      */
40993     getGroupValue : function(){
40994         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40995     },
40996     
40997     
40998     onRender : function(ct, position){
40999         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41000         
41001         if(this.inputValue !== undefined){
41002             this.el.dom.value = this.inputValue;
41003         }
41004          
41005         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41006         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41007         //var viewEl = this.wrap.createChild({ 
41008         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41009         //this.viewEl = viewEl;   
41010         //this.wrap.on('click', this.onClick,  this); 
41011         
41012         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41013         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41014         
41015         
41016         
41017         if(this.boxLabel){
41018             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41019         //    viewEl.on('click', this.onClick,  this); 
41020         }
41021          if(this.checked){
41022             this.el.dom.checked =   'checked' ;
41023         }
41024          
41025     } 
41026     
41027     
41028 });//<script type="text/javascript">
41029
41030 /*
41031  * Based  Ext JS Library 1.1.1
41032  * Copyright(c) 2006-2007, Ext JS, LLC.
41033  * LGPL
41034  *
41035  */
41036  
41037 /**
41038  * @class Roo.HtmlEditorCore
41039  * @extends Roo.Component
41040  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41041  *
41042  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41043  */
41044
41045 Roo.HtmlEditorCore = function(config){
41046     
41047     
41048     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41049     
41050     
41051     this.addEvents({
41052         /**
41053          * @event initialize
41054          * Fires when the editor is fully initialized (including the iframe)
41055          * @param {Roo.HtmlEditorCore} this
41056          */
41057         initialize: true,
41058         /**
41059          * @event activate
41060          * Fires when the editor is first receives the focus. Any insertion must wait
41061          * until after this event.
41062          * @param {Roo.HtmlEditorCore} this
41063          */
41064         activate: true,
41065          /**
41066          * @event beforesync
41067          * Fires before the textarea is updated with content from the editor iframe. Return false
41068          * to cancel the sync.
41069          * @param {Roo.HtmlEditorCore} this
41070          * @param {String} html
41071          */
41072         beforesync: true,
41073          /**
41074          * @event beforepush
41075          * Fires before the iframe editor is updated with content from the textarea. Return false
41076          * to cancel the push.
41077          * @param {Roo.HtmlEditorCore} this
41078          * @param {String} html
41079          */
41080         beforepush: true,
41081          /**
41082          * @event sync
41083          * Fires when the textarea is updated with content from the editor iframe.
41084          * @param {Roo.HtmlEditorCore} this
41085          * @param {String} html
41086          */
41087         sync: true,
41088          /**
41089          * @event push
41090          * Fires when the iframe editor is updated with content from the textarea.
41091          * @param {Roo.HtmlEditorCore} this
41092          * @param {String} html
41093          */
41094         push: true,
41095         
41096         /**
41097          * @event editorevent
41098          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41099          * @param {Roo.HtmlEditorCore} this
41100          */
41101         editorevent: true
41102     });
41103     
41104     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41105     
41106     // defaults : white / black...
41107     this.applyBlacklists();
41108     
41109     
41110     
41111 };
41112
41113
41114 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41115
41116
41117      /**
41118      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41119      */
41120     
41121     owner : false,
41122     
41123      /**
41124      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41125      *                        Roo.resizable.
41126      */
41127     resizable : false,
41128      /**
41129      * @cfg {Number} height (in pixels)
41130      */   
41131     height: 300,
41132    /**
41133      * @cfg {Number} width (in pixels)
41134      */   
41135     width: 500,
41136     
41137     /**
41138      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41139      * 
41140      */
41141     stylesheets: false,
41142     
41143     // id of frame..
41144     frameId: false,
41145     
41146     // private properties
41147     validationEvent : false,
41148     deferHeight: true,
41149     initialized : false,
41150     activated : false,
41151     sourceEditMode : false,
41152     onFocus : Roo.emptyFn,
41153     iframePad:3,
41154     hideMode:'offsets',
41155     
41156     clearUp: true,
41157     
41158     // blacklist + whitelisted elements..
41159     black: false,
41160     white: false,
41161      
41162     
41163
41164     /**
41165      * Protected method that will not generally be called directly. It
41166      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41167      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41168      */
41169     getDocMarkup : function(){
41170         // body styles..
41171         var st = '';
41172         Roo.log(this.stylesheets);
41173         
41174         // inherit styels from page...?? 
41175         if (this.stylesheets === false) {
41176             
41177             Roo.get(document.head).select('style').each(function(node) {
41178                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41179             });
41180             
41181             Roo.get(document.head).select('link').each(function(node) { 
41182                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41183             });
41184             
41185         } else if (!this.stylesheets.length) {
41186                 // simple..
41187                 st = '<style type="text/css">' +
41188                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41189                    '</style>';
41190         } else {
41191             Roo.each(this.stylesheets, function(s) {
41192                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41193             });
41194             
41195         }
41196         
41197         st +=  '<style type="text/css">' +
41198             'IMG { cursor: pointer } ' +
41199         '</style>';
41200
41201         
41202         return '<html><head>' + st  +
41203             //<style type="text/css">' +
41204             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41205             //'</style>' +
41206             ' </head><body class="roo-htmleditor-body"></body></html>';
41207     },
41208
41209     // private
41210     onRender : function(ct, position)
41211     {
41212         var _t = this;
41213         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41214         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41215         
41216         
41217         this.el.dom.style.border = '0 none';
41218         this.el.dom.setAttribute('tabIndex', -1);
41219         this.el.addClass('x-hidden hide');
41220         
41221         
41222         
41223         if(Roo.isIE){ // fix IE 1px bogus margin
41224             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41225         }
41226        
41227         
41228         this.frameId = Roo.id();
41229         
41230          
41231         
41232         var iframe = this.owner.wrap.createChild({
41233             tag: 'iframe',
41234             cls: 'form-control', // bootstrap..
41235             id: this.frameId,
41236             name: this.frameId,
41237             frameBorder : 'no',
41238             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41239         }, this.el
41240         );
41241         
41242         
41243         this.iframe = iframe.dom;
41244
41245          this.assignDocWin();
41246         
41247         this.doc.designMode = 'on';
41248        
41249         this.doc.open();
41250         this.doc.write(this.getDocMarkup());
41251         this.doc.close();
41252
41253         
41254         var task = { // must defer to wait for browser to be ready
41255             run : function(){
41256                 //console.log("run task?" + this.doc.readyState);
41257                 this.assignDocWin();
41258                 if(this.doc.body || this.doc.readyState == 'complete'){
41259                     try {
41260                         this.doc.designMode="on";
41261                     } catch (e) {
41262                         return;
41263                     }
41264                     Roo.TaskMgr.stop(task);
41265                     this.initEditor.defer(10, this);
41266                 }
41267             },
41268             interval : 10,
41269             duration: 10000,
41270             scope: this
41271         };
41272         Roo.TaskMgr.start(task);
41273
41274         
41275          
41276     },
41277
41278     // private
41279     onResize : function(w, h)
41280     {
41281          Roo.log('resize: ' +w + ',' + h );
41282         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41283         if(!this.iframe){
41284             return;
41285         }
41286         if(typeof w == 'number'){
41287             
41288             this.iframe.style.width = w + 'px';
41289         }
41290         if(typeof h == 'number'){
41291             
41292             this.iframe.style.height = h + 'px';
41293             if(this.doc){
41294                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41295             }
41296         }
41297         
41298     },
41299
41300     /**
41301      * Toggles the editor between standard and source edit mode.
41302      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41303      */
41304     toggleSourceEdit : function(sourceEditMode){
41305         
41306         this.sourceEditMode = sourceEditMode === true;
41307         
41308         if(this.sourceEditMode){
41309  
41310             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41311             
41312         }else{
41313             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41314             //this.iframe.className = '';
41315             this.deferFocus();
41316         }
41317         //this.setSize(this.owner.wrap.getSize());
41318         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41319     },
41320
41321     
41322   
41323
41324     /**
41325      * Protected method that will not generally be called directly. If you need/want
41326      * custom HTML cleanup, this is the method you should override.
41327      * @param {String} html The HTML to be cleaned
41328      * return {String} The cleaned HTML
41329      */
41330     cleanHtml : function(html){
41331         html = String(html);
41332         if(html.length > 5){
41333             if(Roo.isSafari){ // strip safari nonsense
41334                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41335             }
41336         }
41337         if(html == '&nbsp;'){
41338             html = '';
41339         }
41340         return html;
41341     },
41342
41343     /**
41344      * HTML Editor -> Textarea
41345      * Protected method that will not generally be called directly. Syncs the contents
41346      * of the editor iframe with the textarea.
41347      */
41348     syncValue : function(){
41349         if(this.initialized){
41350             var bd = (this.doc.body || this.doc.documentElement);
41351             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41352             var html = bd.innerHTML;
41353             if(Roo.isSafari){
41354                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41355                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41356                 if(m && m[1]){
41357                     html = '<div style="'+m[0]+'">' + html + '</div>';
41358                 }
41359             }
41360             html = this.cleanHtml(html);
41361             // fix up the special chars.. normaly like back quotes in word...
41362             // however we do not want to do this with chinese..
41363             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41364                 var cc = b.charCodeAt();
41365                 if (
41366                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41367                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41368                     (cc >= 0xf900 && cc < 0xfb00 )
41369                 ) {
41370                         return b;
41371                 }
41372                 return "&#"+cc+";" 
41373             });
41374             if(this.owner.fireEvent('beforesync', this, html) !== false){
41375                 this.el.dom.value = html;
41376                 this.owner.fireEvent('sync', this, html);
41377             }
41378         }
41379     },
41380
41381     /**
41382      * Protected method that will not generally be called directly. Pushes the value of the textarea
41383      * into the iframe editor.
41384      */
41385     pushValue : function(){
41386         if(this.initialized){
41387             var v = this.el.dom.value.trim();
41388             
41389 //            if(v.length < 1){
41390 //                v = '&#160;';
41391 //            }
41392             
41393             if(this.owner.fireEvent('beforepush', this, v) !== false){
41394                 var d = (this.doc.body || this.doc.documentElement);
41395                 d.innerHTML = v;
41396                 this.cleanUpPaste();
41397                 this.el.dom.value = d.innerHTML;
41398                 this.owner.fireEvent('push', this, v);
41399             }
41400         }
41401     },
41402
41403     // private
41404     deferFocus : function(){
41405         this.focus.defer(10, this);
41406     },
41407
41408     // doc'ed in Field
41409     focus : function(){
41410         if(this.win && !this.sourceEditMode){
41411             this.win.focus();
41412         }else{
41413             this.el.focus();
41414         }
41415     },
41416     
41417     assignDocWin: function()
41418     {
41419         var iframe = this.iframe;
41420         
41421          if(Roo.isIE){
41422             this.doc = iframe.contentWindow.document;
41423             this.win = iframe.contentWindow;
41424         } else {
41425 //            if (!Roo.get(this.frameId)) {
41426 //                return;
41427 //            }
41428 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41429 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41430             
41431             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41432                 return;
41433             }
41434             
41435             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41436             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41437         }
41438     },
41439     
41440     // private
41441     initEditor : function(){
41442         //console.log("INIT EDITOR");
41443         this.assignDocWin();
41444         
41445         
41446         
41447         this.doc.designMode="on";
41448         this.doc.open();
41449         this.doc.write(this.getDocMarkup());
41450         this.doc.close();
41451         
41452         var dbody = (this.doc.body || this.doc.documentElement);
41453         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41454         // this copies styles from the containing element into thsi one..
41455         // not sure why we need all of this..
41456         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41457         
41458         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41459         //ss['background-attachment'] = 'fixed'; // w3c
41460         dbody.bgProperties = 'fixed'; // ie
41461         //Roo.DomHelper.applyStyles(dbody, ss);
41462         Roo.EventManager.on(this.doc, {
41463             //'mousedown': this.onEditorEvent,
41464             'mouseup': this.onEditorEvent,
41465             'dblclick': this.onEditorEvent,
41466             'click': this.onEditorEvent,
41467             'keyup': this.onEditorEvent,
41468             buffer:100,
41469             scope: this
41470         });
41471         if(Roo.isGecko){
41472             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41473         }
41474         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41475             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41476         }
41477         this.initialized = true;
41478
41479         this.owner.fireEvent('initialize', this);
41480         this.pushValue();
41481     },
41482
41483     // private
41484     onDestroy : function(){
41485         
41486         
41487         
41488         if(this.rendered){
41489             
41490             //for (var i =0; i < this.toolbars.length;i++) {
41491             //    // fixme - ask toolbars for heights?
41492             //    this.toolbars[i].onDestroy();
41493            // }
41494             
41495             //this.wrap.dom.innerHTML = '';
41496             //this.wrap.remove();
41497         }
41498     },
41499
41500     // private
41501     onFirstFocus : function(){
41502         
41503         this.assignDocWin();
41504         
41505         
41506         this.activated = true;
41507          
41508     
41509         if(Roo.isGecko){ // prevent silly gecko errors
41510             this.win.focus();
41511             var s = this.win.getSelection();
41512             if(!s.focusNode || s.focusNode.nodeType != 3){
41513                 var r = s.getRangeAt(0);
41514                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41515                 r.collapse(true);
41516                 this.deferFocus();
41517             }
41518             try{
41519                 this.execCmd('useCSS', true);
41520                 this.execCmd('styleWithCSS', false);
41521             }catch(e){}
41522         }
41523         this.owner.fireEvent('activate', this);
41524     },
41525
41526     // private
41527     adjustFont: function(btn){
41528         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41529         //if(Roo.isSafari){ // safari
41530         //    adjust *= 2;
41531        // }
41532         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41533         if(Roo.isSafari){ // safari
41534             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41535             v =  (v < 10) ? 10 : v;
41536             v =  (v > 48) ? 48 : v;
41537             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41538             
41539         }
41540         
41541         
41542         v = Math.max(1, v+adjust);
41543         
41544         this.execCmd('FontSize', v  );
41545     },
41546
41547     onEditorEvent : function(e){
41548         this.owner.fireEvent('editorevent', this, e);
41549       //  this.updateToolbar();
41550         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41551     },
41552
41553     insertTag : function(tg)
41554     {
41555         // could be a bit smarter... -> wrap the current selected tRoo..
41556         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41557             
41558             range = this.createRange(this.getSelection());
41559             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41560             wrappingNode.appendChild(range.extractContents());
41561             range.insertNode(wrappingNode);
41562
41563             return;
41564             
41565             
41566             
41567         }
41568         this.execCmd("formatblock",   tg);
41569         
41570     },
41571     
41572     insertText : function(txt)
41573     {
41574         
41575         
41576         var range = this.createRange();
41577         range.deleteContents();
41578                //alert(Sender.getAttribute('label'));
41579                
41580         range.insertNode(this.doc.createTextNode(txt));
41581     } ,
41582     
41583      
41584
41585     /**
41586      * Executes a Midas editor command on the editor document and performs necessary focus and
41587      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41588      * @param {String} cmd The Midas command
41589      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41590      */
41591     relayCmd : function(cmd, value){
41592         this.win.focus();
41593         this.execCmd(cmd, value);
41594         this.owner.fireEvent('editorevent', this);
41595         //this.updateToolbar();
41596         this.owner.deferFocus();
41597     },
41598
41599     /**
41600      * Executes a Midas editor command directly on the editor document.
41601      * For visual commands, you should use {@link #relayCmd} instead.
41602      * <b>This should only be called after the editor is initialized.</b>
41603      * @param {String} cmd The Midas command
41604      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41605      */
41606     execCmd : function(cmd, value){
41607         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41608         this.syncValue();
41609     },
41610  
41611  
41612    
41613     /**
41614      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41615      * to insert tRoo.
41616      * @param {String} text | dom node.. 
41617      */
41618     insertAtCursor : function(text)
41619     {
41620         
41621         
41622         
41623         if(!this.activated){
41624             return;
41625         }
41626         /*
41627         if(Roo.isIE){
41628             this.win.focus();
41629             var r = this.doc.selection.createRange();
41630             if(r){
41631                 r.collapse(true);
41632                 r.pasteHTML(text);
41633                 this.syncValue();
41634                 this.deferFocus();
41635             
41636             }
41637             return;
41638         }
41639         */
41640         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41641             this.win.focus();
41642             
41643             
41644             // from jquery ui (MIT licenced)
41645             var range, node;
41646             var win = this.win;
41647             
41648             if (win.getSelection && win.getSelection().getRangeAt) {
41649                 range = win.getSelection().getRangeAt(0);
41650                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41651                 range.insertNode(node);
41652             } else if (win.document.selection && win.document.selection.createRange) {
41653                 // no firefox support
41654                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41655                 win.document.selection.createRange().pasteHTML(txt);
41656             } else {
41657                 // no firefox support
41658                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41659                 this.execCmd('InsertHTML', txt);
41660             } 
41661             
41662             this.syncValue();
41663             
41664             this.deferFocus();
41665         }
41666     },
41667  // private
41668     mozKeyPress : function(e){
41669         if(e.ctrlKey){
41670             var c = e.getCharCode(), cmd;
41671           
41672             if(c > 0){
41673                 c = String.fromCharCode(c).toLowerCase();
41674                 switch(c){
41675                     case 'b':
41676                         cmd = 'bold';
41677                         break;
41678                     case 'i':
41679                         cmd = 'italic';
41680                         break;
41681                     
41682                     case 'u':
41683                         cmd = 'underline';
41684                         break;
41685                     
41686                     case 'v':
41687                         this.cleanUpPaste.defer(100, this);
41688                         return;
41689                         
41690                 }
41691                 if(cmd){
41692                     this.win.focus();
41693                     this.execCmd(cmd);
41694                     this.deferFocus();
41695                     e.preventDefault();
41696                 }
41697                 
41698             }
41699         }
41700     },
41701
41702     // private
41703     fixKeys : function(){ // load time branching for fastest keydown performance
41704         if(Roo.isIE){
41705             return function(e){
41706                 var k = e.getKey(), r;
41707                 if(k == e.TAB){
41708                     e.stopEvent();
41709                     r = this.doc.selection.createRange();
41710                     if(r){
41711                         r.collapse(true);
41712                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41713                         this.deferFocus();
41714                     }
41715                     return;
41716                 }
41717                 
41718                 if(k == e.ENTER){
41719                     r = this.doc.selection.createRange();
41720                     if(r){
41721                         var target = r.parentElement();
41722                         if(!target || target.tagName.toLowerCase() != 'li'){
41723                             e.stopEvent();
41724                             r.pasteHTML('<br />');
41725                             r.collapse(false);
41726                             r.select();
41727                         }
41728                     }
41729                 }
41730                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41731                     this.cleanUpPaste.defer(100, this);
41732                     return;
41733                 }
41734                 
41735                 
41736             };
41737         }else if(Roo.isOpera){
41738             return function(e){
41739                 var k = e.getKey();
41740                 if(k == e.TAB){
41741                     e.stopEvent();
41742                     this.win.focus();
41743                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41744                     this.deferFocus();
41745                 }
41746                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41747                     this.cleanUpPaste.defer(100, this);
41748                     return;
41749                 }
41750                 
41751             };
41752         }else if(Roo.isSafari){
41753             return function(e){
41754                 var k = e.getKey();
41755                 
41756                 if(k == e.TAB){
41757                     e.stopEvent();
41758                     this.execCmd('InsertText','\t');
41759                     this.deferFocus();
41760                     return;
41761                 }
41762                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41763                     this.cleanUpPaste.defer(100, this);
41764                     return;
41765                 }
41766                 
41767              };
41768         }
41769     }(),
41770     
41771     getAllAncestors: function()
41772     {
41773         var p = this.getSelectedNode();
41774         var a = [];
41775         if (!p) {
41776             a.push(p); // push blank onto stack..
41777             p = this.getParentElement();
41778         }
41779         
41780         
41781         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41782             a.push(p);
41783             p = p.parentNode;
41784         }
41785         a.push(this.doc.body);
41786         return a;
41787     },
41788     lastSel : false,
41789     lastSelNode : false,
41790     
41791     
41792     getSelection : function() 
41793     {
41794         this.assignDocWin();
41795         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41796     },
41797     
41798     getSelectedNode: function() 
41799     {
41800         // this may only work on Gecko!!!
41801         
41802         // should we cache this!!!!
41803         
41804         
41805         
41806          
41807         var range = this.createRange(this.getSelection()).cloneRange();
41808         
41809         if (Roo.isIE) {
41810             var parent = range.parentElement();
41811             while (true) {
41812                 var testRange = range.duplicate();
41813                 testRange.moveToElementText(parent);
41814                 if (testRange.inRange(range)) {
41815                     break;
41816                 }
41817                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41818                     break;
41819                 }
41820                 parent = parent.parentElement;
41821             }
41822             return parent;
41823         }
41824         
41825         // is ancestor a text element.
41826         var ac =  range.commonAncestorContainer;
41827         if (ac.nodeType == 3) {
41828             ac = ac.parentNode;
41829         }
41830         
41831         var ar = ac.childNodes;
41832          
41833         var nodes = [];
41834         var other_nodes = [];
41835         var has_other_nodes = false;
41836         for (var i=0;i<ar.length;i++) {
41837             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41838                 continue;
41839             }
41840             // fullly contained node.
41841             
41842             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41843                 nodes.push(ar[i]);
41844                 continue;
41845             }
41846             
41847             // probably selected..
41848             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41849                 other_nodes.push(ar[i]);
41850                 continue;
41851             }
41852             // outer..
41853             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41854                 continue;
41855             }
41856             
41857             
41858             has_other_nodes = true;
41859         }
41860         if (!nodes.length && other_nodes.length) {
41861             nodes= other_nodes;
41862         }
41863         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41864             return false;
41865         }
41866         
41867         return nodes[0];
41868     },
41869     createRange: function(sel)
41870     {
41871         // this has strange effects when using with 
41872         // top toolbar - not sure if it's a great idea.
41873         //this.editor.contentWindow.focus();
41874         if (typeof sel != "undefined") {
41875             try {
41876                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41877             } catch(e) {
41878                 return this.doc.createRange();
41879             }
41880         } else {
41881             return this.doc.createRange();
41882         }
41883     },
41884     getParentElement: function()
41885     {
41886         
41887         this.assignDocWin();
41888         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41889         
41890         var range = this.createRange(sel);
41891          
41892         try {
41893             var p = range.commonAncestorContainer;
41894             while (p.nodeType == 3) { // text node
41895                 p = p.parentNode;
41896             }
41897             return p;
41898         } catch (e) {
41899             return null;
41900         }
41901     
41902     },
41903     /***
41904      *
41905      * Range intersection.. the hard stuff...
41906      *  '-1' = before
41907      *  '0' = hits..
41908      *  '1' = after.
41909      *         [ -- selected range --- ]
41910      *   [fail]                        [fail]
41911      *
41912      *    basically..
41913      *      if end is before start or  hits it. fail.
41914      *      if start is after end or hits it fail.
41915      *
41916      *   if either hits (but other is outside. - then it's not 
41917      *   
41918      *    
41919      **/
41920     
41921     
41922     // @see http://www.thismuchiknow.co.uk/?p=64.
41923     rangeIntersectsNode : function(range, node)
41924     {
41925         var nodeRange = node.ownerDocument.createRange();
41926         try {
41927             nodeRange.selectNode(node);
41928         } catch (e) {
41929             nodeRange.selectNodeContents(node);
41930         }
41931     
41932         var rangeStartRange = range.cloneRange();
41933         rangeStartRange.collapse(true);
41934     
41935         var rangeEndRange = range.cloneRange();
41936         rangeEndRange.collapse(false);
41937     
41938         var nodeStartRange = nodeRange.cloneRange();
41939         nodeStartRange.collapse(true);
41940     
41941         var nodeEndRange = nodeRange.cloneRange();
41942         nodeEndRange.collapse(false);
41943     
41944         return rangeStartRange.compareBoundaryPoints(
41945                  Range.START_TO_START, nodeEndRange) == -1 &&
41946                rangeEndRange.compareBoundaryPoints(
41947                  Range.START_TO_START, nodeStartRange) == 1;
41948         
41949          
41950     },
41951     rangeCompareNode : function(range, node)
41952     {
41953         var nodeRange = node.ownerDocument.createRange();
41954         try {
41955             nodeRange.selectNode(node);
41956         } catch (e) {
41957             nodeRange.selectNodeContents(node);
41958         }
41959         
41960         
41961         range.collapse(true);
41962     
41963         nodeRange.collapse(true);
41964      
41965         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41966         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41967          
41968         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41969         
41970         var nodeIsBefore   =  ss == 1;
41971         var nodeIsAfter    = ee == -1;
41972         
41973         if (nodeIsBefore && nodeIsAfter)
41974             return 0; // outer
41975         if (!nodeIsBefore && nodeIsAfter)
41976             return 1; //right trailed.
41977         
41978         if (nodeIsBefore && !nodeIsAfter)
41979             return 2;  // left trailed.
41980         // fully contined.
41981         return 3;
41982     },
41983
41984     // private? - in a new class?
41985     cleanUpPaste :  function()
41986     {
41987         // cleans up the whole document..
41988         Roo.log('cleanuppaste');
41989         
41990         this.cleanUpChildren(this.doc.body);
41991         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41992         if (clean != this.doc.body.innerHTML) {
41993             this.doc.body.innerHTML = clean;
41994         }
41995         
41996     },
41997     
41998     cleanWordChars : function(input) {// change the chars to hex code
41999         var he = Roo.HtmlEditorCore;
42000         
42001         var output = input;
42002         Roo.each(he.swapCodes, function(sw) { 
42003             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42004             
42005             output = output.replace(swapper, sw[1]);
42006         });
42007         
42008         return output;
42009     },
42010     
42011     
42012     cleanUpChildren : function (n)
42013     {
42014         if (!n.childNodes.length) {
42015             return;
42016         }
42017         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42018            this.cleanUpChild(n.childNodes[i]);
42019         }
42020     },
42021     
42022     
42023         
42024     
42025     cleanUpChild : function (node)
42026     {
42027         var ed = this;
42028         //console.log(node);
42029         if (node.nodeName == "#text") {
42030             // clean up silly Windows -- stuff?
42031             return; 
42032         }
42033         if (node.nodeName == "#comment") {
42034             node.parentNode.removeChild(node);
42035             // clean up silly Windows -- stuff?
42036             return; 
42037         }
42038         var lcname = node.tagName.toLowerCase();
42039         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42040         // whitelist of tags..
42041         
42042         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42043             // remove node.
42044             node.parentNode.removeChild(node);
42045             return;
42046             
42047         }
42048         
42049         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42050         
42051         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42052         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42053         
42054         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42055         //    remove_keep_children = true;
42056         //}
42057         
42058         if (remove_keep_children) {
42059             this.cleanUpChildren(node);
42060             // inserts everything just before this node...
42061             while (node.childNodes.length) {
42062                 var cn = node.childNodes[0];
42063                 node.removeChild(cn);
42064                 node.parentNode.insertBefore(cn, node);
42065             }
42066             node.parentNode.removeChild(node);
42067             return;
42068         }
42069         
42070         if (!node.attributes || !node.attributes.length) {
42071             this.cleanUpChildren(node);
42072             return;
42073         }
42074         
42075         function cleanAttr(n,v)
42076         {
42077             
42078             if (v.match(/^\./) || v.match(/^\//)) {
42079                 return;
42080             }
42081             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42082                 return;
42083             }
42084             if (v.match(/^#/)) {
42085                 return;
42086             }
42087 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42088             node.removeAttribute(n);
42089             
42090         }
42091         
42092         var cwhite = this.cwhite;
42093         var cblack = this.cblack;
42094             
42095         function cleanStyle(n,v)
42096         {
42097             if (v.match(/expression/)) { //XSS?? should we even bother..
42098                 node.removeAttribute(n);
42099                 return;
42100             }
42101             
42102             var parts = v.split(/;/);
42103             var clean = [];
42104             
42105             Roo.each(parts, function(p) {
42106                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42107                 if (!p.length) {
42108                     return true;
42109                 }
42110                 var l = p.split(':').shift().replace(/\s+/g,'');
42111                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42112                 
42113                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42114 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42115                     //node.removeAttribute(n);
42116                     return true;
42117                 }
42118                 //Roo.log()
42119                 // only allow 'c whitelisted system attributes'
42120                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42121 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42122                     //node.removeAttribute(n);
42123                     return true;
42124                 }
42125                 
42126                 
42127                  
42128                 
42129                 clean.push(p);
42130                 return true;
42131             });
42132             if (clean.length) { 
42133                 node.setAttribute(n, clean.join(';'));
42134             } else {
42135                 node.removeAttribute(n);
42136             }
42137             
42138         }
42139         
42140         
42141         for (var i = node.attributes.length-1; i > -1 ; i--) {
42142             var a = node.attributes[i];
42143             //console.log(a);
42144             
42145             if (a.name.toLowerCase().substr(0,2)=='on')  {
42146                 node.removeAttribute(a.name);
42147                 continue;
42148             }
42149             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42150                 node.removeAttribute(a.name);
42151                 continue;
42152             }
42153             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42154                 cleanAttr(a.name,a.value); // fixme..
42155                 continue;
42156             }
42157             if (a.name == 'style') {
42158                 cleanStyle(a.name,a.value);
42159                 continue;
42160             }
42161             /// clean up MS crap..
42162             // tecnically this should be a list of valid class'es..
42163             
42164             
42165             if (a.name == 'class') {
42166                 if (a.value.match(/^Mso/)) {
42167                     node.className = '';
42168                 }
42169                 
42170                 if (a.value.match(/body/)) {
42171                     node.className = '';
42172                 }
42173                 continue;
42174             }
42175             
42176             // style cleanup!?
42177             // class cleanup?
42178             
42179         }
42180         
42181         
42182         this.cleanUpChildren(node);
42183         
42184         
42185     },
42186     /**
42187      * Clean up MS wordisms...
42188      */
42189     cleanWord : function(node)
42190     {
42191         var _t = this;
42192         var cleanWordChildren = function()
42193         {
42194             if (!node.childNodes.length) {
42195                 return;
42196             }
42197             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42198                _t.cleanWord(node.childNodes[i]);
42199             }
42200         }
42201         
42202         
42203         if (!node) {
42204             this.cleanWord(this.doc.body);
42205             return;
42206         }
42207         if (node.nodeName == "#text") {
42208             // clean up silly Windows -- stuff?
42209             return; 
42210         }
42211         if (node.nodeName == "#comment") {
42212             node.parentNode.removeChild(node);
42213             // clean up silly Windows -- stuff?
42214             return; 
42215         }
42216         
42217         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42218             node.parentNode.removeChild(node);
42219             return;
42220         }
42221         
42222         // remove - but keep children..
42223         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42224             while (node.childNodes.length) {
42225                 var cn = node.childNodes[0];
42226                 node.removeChild(cn);
42227                 node.parentNode.insertBefore(cn, node);
42228             }
42229             node.parentNode.removeChild(node);
42230             cleanWordChildren();
42231             return;
42232         }
42233         // clean styles
42234         if (node.className.length) {
42235             
42236             var cn = node.className.split(/\W+/);
42237             var cna = [];
42238             Roo.each(cn, function(cls) {
42239                 if (cls.match(/Mso[a-zA-Z]+/)) {
42240                     return;
42241                 }
42242                 cna.push(cls);
42243             });
42244             node.className = cna.length ? cna.join(' ') : '';
42245             if (!cna.length) {
42246                 node.removeAttribute("class");
42247             }
42248         }
42249         
42250         if (node.hasAttribute("lang")) {
42251             node.removeAttribute("lang");
42252         }
42253         
42254         if (node.hasAttribute("style")) {
42255             
42256             var styles = node.getAttribute("style").split(";");
42257             var nstyle = [];
42258             Roo.each(styles, function(s) {
42259                 if (!s.match(/:/)) {
42260                     return;
42261                 }
42262                 var kv = s.split(":");
42263                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42264                     return;
42265                 }
42266                 // what ever is left... we allow.
42267                 nstyle.push(s);
42268             });
42269             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42270             if (!nstyle.length) {
42271                 node.removeAttribute('style');
42272             }
42273         }
42274         
42275         cleanWordChildren();
42276         
42277         
42278     },
42279     domToHTML : function(currentElement, depth, nopadtext) {
42280         
42281         depth = depth || 0;
42282         nopadtext = nopadtext || false;
42283     
42284         if (!currentElement) {
42285             return this.domToHTML(this.doc.body);
42286         }
42287         
42288         //Roo.log(currentElement);
42289         var j;
42290         var allText = false;
42291         var nodeName = currentElement.nodeName;
42292         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42293         
42294         if  (nodeName == '#text') {
42295             return currentElement.nodeValue;
42296         }
42297         
42298         
42299         var ret = '';
42300         if (nodeName != 'BODY') {
42301              
42302             var i = 0;
42303             // Prints the node tagName, such as <A>, <IMG>, etc
42304             if (tagName) {
42305                 var attr = [];
42306                 for(i = 0; i < currentElement.attributes.length;i++) {
42307                     // quoting?
42308                     var aname = currentElement.attributes.item(i).name;
42309                     if (!currentElement.attributes.item(i).value.length) {
42310                         continue;
42311                     }
42312                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42313                 }
42314                 
42315                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42316             } 
42317             else {
42318                 
42319                 // eack
42320             }
42321         } else {
42322             tagName = false;
42323         }
42324         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42325             return ret;
42326         }
42327         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42328             nopadtext = true;
42329         }
42330         
42331         
42332         // Traverse the tree
42333         i = 0;
42334         var currentElementChild = currentElement.childNodes.item(i);
42335         var allText = true;
42336         var innerHTML  = '';
42337         lastnode = '';
42338         while (currentElementChild) {
42339             // Formatting code (indent the tree so it looks nice on the screen)
42340             var nopad = nopadtext;
42341             if (lastnode == 'SPAN') {
42342                 nopad  = true;
42343             }
42344             // text
42345             if  (currentElementChild.nodeName == '#text') {
42346                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42347                 if (!nopad && toadd.length > 80) {
42348                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42349                 }
42350                 innerHTML  += toadd;
42351                 
42352                 i++;
42353                 currentElementChild = currentElement.childNodes.item(i);
42354                 lastNode = '';
42355                 continue;
42356             }
42357             allText = false;
42358             
42359             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42360                 
42361             // Recursively traverse the tree structure of the child node
42362             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42363             lastnode = currentElementChild.nodeName;
42364             i++;
42365             currentElementChild=currentElement.childNodes.item(i);
42366         }
42367         
42368         ret += innerHTML;
42369         
42370         if (!allText) {
42371                 // The remaining code is mostly for formatting the tree
42372             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42373         }
42374         
42375         
42376         if (tagName) {
42377             ret+= "</"+tagName+">";
42378         }
42379         return ret;
42380         
42381     },
42382         
42383     applyBlacklists : function()
42384     {
42385         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42386         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42387         
42388         this.white = [];
42389         this.black = [];
42390         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42391             if (b.indexOf(tag) > -1) {
42392                 return;
42393             }
42394             this.white.push(tag);
42395             
42396         }, this);
42397         
42398         Roo.each(w, function(tag) {
42399             if (b.indexOf(tag) > -1) {
42400                 return;
42401             }
42402             if (this.white.indexOf(tag) > -1) {
42403                 return;
42404             }
42405             this.white.push(tag);
42406             
42407         }, this);
42408         
42409         
42410         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42411             if (w.indexOf(tag) > -1) {
42412                 return;
42413             }
42414             this.black.push(tag);
42415             
42416         }, this);
42417         
42418         Roo.each(b, function(tag) {
42419             if (w.indexOf(tag) > -1) {
42420                 return;
42421             }
42422             if (this.black.indexOf(tag) > -1) {
42423                 return;
42424             }
42425             this.black.push(tag);
42426             
42427         }, this);
42428         
42429         
42430         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42431         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42432         
42433         this.cwhite = [];
42434         this.cblack = [];
42435         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42436             if (b.indexOf(tag) > -1) {
42437                 return;
42438             }
42439             this.cwhite.push(tag);
42440             
42441         }, this);
42442         
42443         Roo.each(w, function(tag) {
42444             if (b.indexOf(tag) > -1) {
42445                 return;
42446             }
42447             if (this.cwhite.indexOf(tag) > -1) {
42448                 return;
42449             }
42450             this.cwhite.push(tag);
42451             
42452         }, this);
42453         
42454         
42455         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42456             if (w.indexOf(tag) > -1) {
42457                 return;
42458             }
42459             this.cblack.push(tag);
42460             
42461         }, this);
42462         
42463         Roo.each(b, function(tag) {
42464             if (w.indexOf(tag) > -1) {
42465                 return;
42466             }
42467             if (this.cblack.indexOf(tag) > -1) {
42468                 return;
42469             }
42470             this.cblack.push(tag);
42471             
42472         }, this);
42473     }
42474     
42475     // hide stuff that is not compatible
42476     /**
42477      * @event blur
42478      * @hide
42479      */
42480     /**
42481      * @event change
42482      * @hide
42483      */
42484     /**
42485      * @event focus
42486      * @hide
42487      */
42488     /**
42489      * @event specialkey
42490      * @hide
42491      */
42492     /**
42493      * @cfg {String} fieldClass @hide
42494      */
42495     /**
42496      * @cfg {String} focusClass @hide
42497      */
42498     /**
42499      * @cfg {String} autoCreate @hide
42500      */
42501     /**
42502      * @cfg {String} inputType @hide
42503      */
42504     /**
42505      * @cfg {String} invalidClass @hide
42506      */
42507     /**
42508      * @cfg {String} invalidText @hide
42509      */
42510     /**
42511      * @cfg {String} msgFx @hide
42512      */
42513     /**
42514      * @cfg {String} validateOnBlur @hide
42515      */
42516 });
42517
42518 Roo.HtmlEditorCore.white = [
42519         'area', 'br', 'img', 'input', 'hr', 'wbr',
42520         
42521        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42522        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42523        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42524        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42525        'table',   'ul',         'xmp', 
42526        
42527        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42528       'thead',   'tr', 
42529      
42530       'dir', 'menu', 'ol', 'ul', 'dl',
42531        
42532       'embed',  'object'
42533 ];
42534
42535
42536 Roo.HtmlEditorCore.black = [
42537     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42538         'applet', // 
42539         'base',   'basefont', 'bgsound', 'blink',  'body', 
42540         'frame',  'frameset', 'head',    'html',   'ilayer', 
42541         'iframe', 'layer',  'link',     'meta',    'object',   
42542         'script', 'style' ,'title',  'xml' // clean later..
42543 ];
42544 Roo.HtmlEditorCore.clean = [
42545     'script', 'style', 'title', 'xml'
42546 ];
42547 Roo.HtmlEditorCore.remove = [
42548     'font'
42549 ];
42550 // attributes..
42551
42552 Roo.HtmlEditorCore.ablack = [
42553     'on'
42554 ];
42555     
42556 Roo.HtmlEditorCore.aclean = [ 
42557     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42558 ];
42559
42560 // protocols..
42561 Roo.HtmlEditorCore.pwhite= [
42562         'http',  'https',  'mailto'
42563 ];
42564
42565 // white listed style attributes.
42566 Roo.HtmlEditorCore.cwhite= [
42567       //  'text-align', /// default is to allow most things..
42568       
42569          
42570 //        'font-size'//??
42571 ];
42572
42573 // black listed style attributes.
42574 Roo.HtmlEditorCore.cblack= [
42575       //  'font-size' -- this can be set by the project 
42576 ];
42577
42578
42579 Roo.HtmlEditorCore.swapCodes   =[ 
42580     [    8211, "--" ], 
42581     [    8212, "--" ], 
42582     [    8216,  "'" ],  
42583     [    8217, "'" ],  
42584     [    8220, '"' ],  
42585     [    8221, '"' ],  
42586     [    8226, "*" ],  
42587     [    8230, "..." ]
42588 ]; 
42589
42590     //<script type="text/javascript">
42591
42592 /*
42593  * Ext JS Library 1.1.1
42594  * Copyright(c) 2006-2007, Ext JS, LLC.
42595  * Licence LGPL
42596  * 
42597  */
42598  
42599  
42600 Roo.form.HtmlEditor = function(config){
42601     
42602     
42603     
42604     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42605     
42606     if (!this.toolbars) {
42607         this.toolbars = [];
42608     }
42609     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42610     
42611     
42612 };
42613
42614 /**
42615  * @class Roo.form.HtmlEditor
42616  * @extends Roo.form.Field
42617  * Provides a lightweight HTML Editor component.
42618  *
42619  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42620  * 
42621  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42622  * supported by this editor.</b><br/><br/>
42623  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42624  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42625  */
42626 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42627     /**
42628      * @cfg {Boolean} clearUp
42629      */
42630     clearUp : true,
42631       /**
42632      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42633      */
42634     toolbars : false,
42635    
42636      /**
42637      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42638      *                        Roo.resizable.
42639      */
42640     resizable : false,
42641      /**
42642      * @cfg {Number} height (in pixels)
42643      */   
42644     height: 300,
42645    /**
42646      * @cfg {Number} width (in pixels)
42647      */   
42648     width: 500,
42649     
42650     /**
42651      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42652      * 
42653      */
42654     stylesheets: false,
42655     
42656     
42657      /**
42658      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42659      * 
42660      */
42661     cblack: false,
42662     /**
42663      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42664      * 
42665      */
42666     cwhite: false,
42667     
42668      /**
42669      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42670      * 
42671      */
42672     black: false,
42673     /**
42674      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42675      * 
42676      */
42677     white: false,
42678     
42679     // id of frame..
42680     frameId: false,
42681     
42682     // private properties
42683     validationEvent : false,
42684     deferHeight: true,
42685     initialized : false,
42686     activated : false,
42687     
42688     onFocus : Roo.emptyFn,
42689     iframePad:3,
42690     hideMode:'offsets',
42691     
42692     actionMode : 'container', // defaults to hiding it...
42693     
42694     defaultAutoCreate : { // modified by initCompnoent..
42695         tag: "textarea",
42696         style:"width:500px;height:300px;",
42697         autocomplete: "off"
42698     },
42699
42700     // private
42701     initComponent : function(){
42702         this.addEvents({
42703             /**
42704              * @event initialize
42705              * Fires when the editor is fully initialized (including the iframe)
42706              * @param {HtmlEditor} this
42707              */
42708             initialize: true,
42709             /**
42710              * @event activate
42711              * Fires when the editor is first receives the focus. Any insertion must wait
42712              * until after this event.
42713              * @param {HtmlEditor} this
42714              */
42715             activate: true,
42716              /**
42717              * @event beforesync
42718              * Fires before the textarea is updated with content from the editor iframe. Return false
42719              * to cancel the sync.
42720              * @param {HtmlEditor} this
42721              * @param {String} html
42722              */
42723             beforesync: true,
42724              /**
42725              * @event beforepush
42726              * Fires before the iframe editor is updated with content from the textarea. Return false
42727              * to cancel the push.
42728              * @param {HtmlEditor} this
42729              * @param {String} html
42730              */
42731             beforepush: true,
42732              /**
42733              * @event sync
42734              * Fires when the textarea is updated with content from the editor iframe.
42735              * @param {HtmlEditor} this
42736              * @param {String} html
42737              */
42738             sync: true,
42739              /**
42740              * @event push
42741              * Fires when the iframe editor is updated with content from the textarea.
42742              * @param {HtmlEditor} this
42743              * @param {String} html
42744              */
42745             push: true,
42746              /**
42747              * @event editmodechange
42748              * Fires when the editor switches edit modes
42749              * @param {HtmlEditor} this
42750              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42751              */
42752             editmodechange: true,
42753             /**
42754              * @event editorevent
42755              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42756              * @param {HtmlEditor} this
42757              */
42758             editorevent: true,
42759             /**
42760              * @event firstfocus
42761              * Fires when on first focus - needed by toolbars..
42762              * @param {HtmlEditor} this
42763              */
42764             firstfocus: true,
42765             /**
42766              * @event autosave
42767              * Auto save the htmlEditor value as a file into Events
42768              * @param {HtmlEditor} this
42769              */
42770             autosave: true,
42771             /**
42772              * @event savedpreview
42773              * preview the saved version of htmlEditor
42774              * @param {HtmlEditor} this
42775              */
42776             savedpreview: true
42777         });
42778         this.defaultAutoCreate =  {
42779             tag: "textarea",
42780             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42781             autocomplete: "off"
42782         };
42783     },
42784
42785     /**
42786      * Protected method that will not generally be called directly. It
42787      * is called when the editor creates its toolbar. Override this method if you need to
42788      * add custom toolbar buttons.
42789      * @param {HtmlEditor} editor
42790      */
42791     createToolbar : function(editor){
42792         Roo.log("create toolbars");
42793         if (!editor.toolbars || !editor.toolbars.length) {
42794             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42795         }
42796         
42797         for (var i =0 ; i < editor.toolbars.length;i++) {
42798             editor.toolbars[i] = Roo.factory(
42799                     typeof(editor.toolbars[i]) == 'string' ?
42800                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42801                 Roo.form.HtmlEditor);
42802             editor.toolbars[i].init(editor);
42803         }
42804          
42805         
42806     },
42807
42808      
42809     // private
42810     onRender : function(ct, position)
42811     {
42812         var _t = this;
42813         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42814         
42815         this.wrap = this.el.wrap({
42816             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42817         });
42818         
42819         this.editorcore.onRender(ct, position);
42820          
42821         if (this.resizable) {
42822             this.resizeEl = new Roo.Resizable(this.wrap, {
42823                 pinned : true,
42824                 wrap: true,
42825                 dynamic : true,
42826                 minHeight : this.height,
42827                 height: this.height,
42828                 handles : this.resizable,
42829                 width: this.width,
42830                 listeners : {
42831                     resize : function(r, w, h) {
42832                         _t.onResize(w,h); // -something
42833                     }
42834                 }
42835             });
42836             
42837         }
42838         this.createToolbar(this);
42839        
42840         
42841         if(!this.width){
42842             this.setSize(this.wrap.getSize());
42843         }
42844         if (this.resizeEl) {
42845             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42846             // should trigger onReize..
42847         }
42848         
42849 //        if(this.autosave && this.w){
42850 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42851 //        }
42852     },
42853
42854     // private
42855     onResize : function(w, h)
42856     {
42857         //Roo.log('resize: ' +w + ',' + h );
42858         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42859         var ew = false;
42860         var eh = false;
42861         
42862         if(this.el ){
42863             if(typeof w == 'number'){
42864                 var aw = w - this.wrap.getFrameWidth('lr');
42865                 this.el.setWidth(this.adjustWidth('textarea', aw));
42866                 ew = aw;
42867             }
42868             if(typeof h == 'number'){
42869                 var tbh = 0;
42870                 for (var i =0; i < this.toolbars.length;i++) {
42871                     // fixme - ask toolbars for heights?
42872                     tbh += this.toolbars[i].tb.el.getHeight();
42873                     if (this.toolbars[i].footer) {
42874                         tbh += this.toolbars[i].footer.el.getHeight();
42875                     }
42876                 }
42877                 
42878                 
42879                 
42880                 
42881                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42882                 ah -= 5; // knock a few pixes off for look..
42883                 this.el.setHeight(this.adjustWidth('textarea', ah));
42884                 var eh = ah;
42885             }
42886         }
42887         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42888         this.editorcore.onResize(ew,eh);
42889         
42890     },
42891
42892     /**
42893      * Toggles the editor between standard and source edit mode.
42894      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42895      */
42896     toggleSourceEdit : function(sourceEditMode)
42897     {
42898         this.editorcore.toggleSourceEdit(sourceEditMode);
42899         
42900         if(this.editorcore.sourceEditMode){
42901             Roo.log('editor - showing textarea');
42902             
42903 //            Roo.log('in');
42904 //            Roo.log(this.syncValue());
42905             this.editorcore.syncValue();
42906             this.el.removeClass('x-hidden');
42907             this.el.dom.removeAttribute('tabIndex');
42908             this.el.focus();
42909         }else{
42910             Roo.log('editor - hiding textarea');
42911 //            Roo.log('out')
42912 //            Roo.log(this.pushValue()); 
42913             this.editorcore.pushValue();
42914             
42915             this.el.addClass('x-hidden');
42916             this.el.dom.setAttribute('tabIndex', -1);
42917             //this.deferFocus();
42918         }
42919          
42920         this.setSize(this.wrap.getSize());
42921         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42922     },
42923  
42924     // private (for BoxComponent)
42925     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42926
42927     // private (for BoxComponent)
42928     getResizeEl : function(){
42929         return this.wrap;
42930     },
42931
42932     // private (for BoxComponent)
42933     getPositionEl : function(){
42934         return this.wrap;
42935     },
42936
42937     // private
42938     initEvents : function(){
42939         this.originalValue = this.getValue();
42940     },
42941
42942     /**
42943      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42944      * @method
42945      */
42946     markInvalid : Roo.emptyFn,
42947     /**
42948      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42949      * @method
42950      */
42951     clearInvalid : Roo.emptyFn,
42952
42953     setValue : function(v){
42954         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42955         this.editorcore.pushValue();
42956     },
42957
42958      
42959     // private
42960     deferFocus : function(){
42961         this.focus.defer(10, this);
42962     },
42963
42964     // doc'ed in Field
42965     focus : function(){
42966         this.editorcore.focus();
42967         
42968     },
42969       
42970
42971     // private
42972     onDestroy : function(){
42973         
42974         
42975         
42976         if(this.rendered){
42977             
42978             for (var i =0; i < this.toolbars.length;i++) {
42979                 // fixme - ask toolbars for heights?
42980                 this.toolbars[i].onDestroy();
42981             }
42982             
42983             this.wrap.dom.innerHTML = '';
42984             this.wrap.remove();
42985         }
42986     },
42987
42988     // private
42989     onFirstFocus : function(){
42990         //Roo.log("onFirstFocus");
42991         this.editorcore.onFirstFocus();
42992          for (var i =0; i < this.toolbars.length;i++) {
42993             this.toolbars[i].onFirstFocus();
42994         }
42995         
42996     },
42997     
42998     // private
42999     syncValue : function()
43000     {
43001         this.editorcore.syncValue();
43002     },
43003     
43004     pushValue : function()
43005     {
43006         this.editorcore.pushValue();
43007     }
43008      
43009     
43010     // hide stuff that is not compatible
43011     /**
43012      * @event blur
43013      * @hide
43014      */
43015     /**
43016      * @event change
43017      * @hide
43018      */
43019     /**
43020      * @event focus
43021      * @hide
43022      */
43023     /**
43024      * @event specialkey
43025      * @hide
43026      */
43027     /**
43028      * @cfg {String} fieldClass @hide
43029      */
43030     /**
43031      * @cfg {String} focusClass @hide
43032      */
43033     /**
43034      * @cfg {String} autoCreate @hide
43035      */
43036     /**
43037      * @cfg {String} inputType @hide
43038      */
43039     /**
43040      * @cfg {String} invalidClass @hide
43041      */
43042     /**
43043      * @cfg {String} invalidText @hide
43044      */
43045     /**
43046      * @cfg {String} msgFx @hide
43047      */
43048     /**
43049      * @cfg {String} validateOnBlur @hide
43050      */
43051 });
43052  
43053     // <script type="text/javascript">
43054 /*
43055  * Based on
43056  * Ext JS Library 1.1.1
43057  * Copyright(c) 2006-2007, Ext JS, LLC.
43058  *  
43059  
43060  */
43061
43062 /**
43063  * @class Roo.form.HtmlEditorToolbar1
43064  * Basic Toolbar
43065  * 
43066  * Usage:
43067  *
43068  new Roo.form.HtmlEditor({
43069     ....
43070     toolbars : [
43071         new Roo.form.HtmlEditorToolbar1({
43072             disable : { fonts: 1 , format: 1, ..., ... , ...],
43073             btns : [ .... ]
43074         })
43075     }
43076      
43077  * 
43078  * @cfg {Object} disable List of elements to disable..
43079  * @cfg {Array} btns List of additional buttons.
43080  * 
43081  * 
43082  * NEEDS Extra CSS? 
43083  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43084  */
43085  
43086 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43087 {
43088     
43089     Roo.apply(this, config);
43090     
43091     // default disabled, based on 'good practice'..
43092     this.disable = this.disable || {};
43093     Roo.applyIf(this.disable, {
43094         fontSize : true,
43095         colors : true,
43096         specialElements : true
43097     });
43098     
43099     
43100     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43101     // dont call parent... till later.
43102 }
43103
43104 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43105     
43106     tb: false,
43107     
43108     rendered: false,
43109     
43110     editor : false,
43111     editorcore : false,
43112     /**
43113      * @cfg {Object} disable  List of toolbar elements to disable
43114          
43115      */
43116     disable : false,
43117     
43118     
43119      /**
43120      * @cfg {String} createLinkText The default text for the create link prompt
43121      */
43122     createLinkText : 'Please enter the URL for the link:',
43123     /**
43124      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43125      */
43126     defaultLinkValue : 'http:/'+'/',
43127    
43128     
43129       /**
43130      * @cfg {Array} fontFamilies An array of available font families
43131      */
43132     fontFamilies : [
43133         'Arial',
43134         'Courier New',
43135         'Tahoma',
43136         'Times New Roman',
43137         'Verdana'
43138     ],
43139     
43140     specialChars : [
43141            "&#169;",
43142           "&#174;",     
43143           "&#8482;",    
43144           "&#163;" ,    
43145          // "&#8212;",    
43146           "&#8230;",    
43147           "&#247;" ,    
43148         //  "&#225;" ,     ?? a acute?
43149            "&#8364;"    , //Euro
43150        //   "&#8220;"    ,
43151         //  "&#8221;"    ,
43152         //  "&#8226;"    ,
43153           "&#176;"  //   , // degrees
43154
43155          // "&#233;"     , // e ecute
43156          // "&#250;"     , // u ecute?
43157     ],
43158     
43159     specialElements : [
43160         {
43161             text: "Insert Table",
43162             xtype: 'MenuItem',
43163             xns : Roo.Menu,
43164             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43165                 
43166         },
43167         {    
43168             text: "Insert Image",
43169             xtype: 'MenuItem',
43170             xns : Roo.Menu,
43171             ihtml : '<img src="about:blank"/>'
43172             
43173         }
43174         
43175          
43176     ],
43177     
43178     
43179     inputElements : [ 
43180             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43181             "input:submit", "input:button", "select", "textarea", "label" ],
43182     formats : [
43183         ["p"] ,  
43184         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43185         ["pre"],[ "code"], 
43186         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43187         ['div'],['span']
43188     ],
43189     
43190     cleanStyles : [
43191         "font-size"
43192     ],
43193      /**
43194      * @cfg {String} defaultFont default font to use.
43195      */
43196     defaultFont: 'tahoma',
43197    
43198     fontSelect : false,
43199     
43200     
43201     formatCombo : false,
43202     
43203     init : function(editor)
43204     {
43205         this.editor = editor;
43206         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43207         var editorcore = this.editorcore;
43208         
43209         var _t = this;
43210         
43211         var fid = editorcore.frameId;
43212         var etb = this;
43213         function btn(id, toggle, handler){
43214             var xid = fid + '-'+ id ;
43215             return {
43216                 id : xid,
43217                 cmd : id,
43218                 cls : 'x-btn-icon x-edit-'+id,
43219                 enableToggle:toggle !== false,
43220                 scope: _t, // was editor...
43221                 handler:handler||_t.relayBtnCmd,
43222                 clickEvent:'mousedown',
43223                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43224                 tabIndex:-1
43225             };
43226         }
43227         
43228         
43229         
43230         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43231         this.tb = tb;
43232          // stop form submits
43233         tb.el.on('click', function(e){
43234             e.preventDefault(); // what does this do?
43235         });
43236
43237         if(!this.disable.font) { // && !Roo.isSafari){
43238             /* why no safari for fonts 
43239             editor.fontSelect = tb.el.createChild({
43240                 tag:'select',
43241                 tabIndex: -1,
43242                 cls:'x-font-select',
43243                 html: this.createFontOptions()
43244             });
43245             
43246             editor.fontSelect.on('change', function(){
43247                 var font = editor.fontSelect.dom.value;
43248                 editor.relayCmd('fontname', font);
43249                 editor.deferFocus();
43250             }, editor);
43251             
43252             tb.add(
43253                 editor.fontSelect.dom,
43254                 '-'
43255             );
43256             */
43257             
43258         };
43259         if(!this.disable.formats){
43260             this.formatCombo = new Roo.form.ComboBox({
43261                 store: new Roo.data.SimpleStore({
43262                     id : 'tag',
43263                     fields: ['tag'],
43264                     data : this.formats // from states.js
43265                 }),
43266                 blockFocus : true,
43267                 name : '',
43268                 //autoCreate : {tag: "div",  size: "20"},
43269                 displayField:'tag',
43270                 typeAhead: false,
43271                 mode: 'local',
43272                 editable : false,
43273                 triggerAction: 'all',
43274                 emptyText:'Add tag',
43275                 selectOnFocus:true,
43276                 width:135,
43277                 listeners : {
43278                     'select': function(c, r, i) {
43279                         editorcore.insertTag(r.get('tag'));
43280                         editor.focus();
43281                     }
43282                 }
43283
43284             });
43285             tb.addField(this.formatCombo);
43286             
43287         }
43288         
43289         if(!this.disable.format){
43290             tb.add(
43291                 btn('bold'),
43292                 btn('italic'),
43293                 btn('underline')
43294             );
43295         };
43296         if(!this.disable.fontSize){
43297             tb.add(
43298                 '-',
43299                 
43300                 
43301                 btn('increasefontsize', false, editorcore.adjustFont),
43302                 btn('decreasefontsize', false, editorcore.adjustFont)
43303             );
43304         };
43305         
43306         
43307         if(!this.disable.colors){
43308             tb.add(
43309                 '-', {
43310                     id:editorcore.frameId +'-forecolor',
43311                     cls:'x-btn-icon x-edit-forecolor',
43312                     clickEvent:'mousedown',
43313                     tooltip: this.buttonTips['forecolor'] || undefined,
43314                     tabIndex:-1,
43315                     menu : new Roo.menu.ColorMenu({
43316                         allowReselect: true,
43317                         focus: Roo.emptyFn,
43318                         value:'000000',
43319                         plain:true,
43320                         selectHandler: function(cp, color){
43321                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43322                             editor.deferFocus();
43323                         },
43324                         scope: editorcore,
43325                         clickEvent:'mousedown'
43326                     })
43327                 }, {
43328                     id:editorcore.frameId +'backcolor',
43329                     cls:'x-btn-icon x-edit-backcolor',
43330                     clickEvent:'mousedown',
43331                     tooltip: this.buttonTips['backcolor'] || undefined,
43332                     tabIndex:-1,
43333                     menu : new Roo.menu.ColorMenu({
43334                         focus: Roo.emptyFn,
43335                         value:'FFFFFF',
43336                         plain:true,
43337                         allowReselect: true,
43338                         selectHandler: function(cp, color){
43339                             if(Roo.isGecko){
43340                                 editorcore.execCmd('useCSS', false);
43341                                 editorcore.execCmd('hilitecolor', color);
43342                                 editorcore.execCmd('useCSS', true);
43343                                 editor.deferFocus();
43344                             }else{
43345                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43346                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43347                                 editor.deferFocus();
43348                             }
43349                         },
43350                         scope:editorcore,
43351                         clickEvent:'mousedown'
43352                     })
43353                 }
43354             );
43355         };
43356         // now add all the items...
43357         
43358
43359         if(!this.disable.alignments){
43360             tb.add(
43361                 '-',
43362                 btn('justifyleft'),
43363                 btn('justifycenter'),
43364                 btn('justifyright')
43365             );
43366         };
43367
43368         //if(!Roo.isSafari){
43369             if(!this.disable.links){
43370                 tb.add(
43371                     '-',
43372                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43373                 );
43374             };
43375
43376             if(!this.disable.lists){
43377                 tb.add(
43378                     '-',
43379                     btn('insertorderedlist'),
43380                     btn('insertunorderedlist')
43381                 );
43382             }
43383             if(!this.disable.sourceEdit){
43384                 tb.add(
43385                     '-',
43386                     btn('sourceedit', true, function(btn){
43387                         Roo.log(this);
43388                         this.toggleSourceEdit(btn.pressed);
43389                     })
43390                 );
43391             }
43392         //}
43393         
43394         var smenu = { };
43395         // special menu.. - needs to be tidied up..
43396         if (!this.disable.special) {
43397             smenu = {
43398                 text: "&#169;",
43399                 cls: 'x-edit-none',
43400                 
43401                 menu : {
43402                     items : []
43403                 }
43404             };
43405             for (var i =0; i < this.specialChars.length; i++) {
43406                 smenu.menu.items.push({
43407                     
43408                     html: this.specialChars[i],
43409                     handler: function(a,b) {
43410                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43411                         //editor.insertAtCursor(a.html);
43412                         
43413                     },
43414                     tabIndex:-1
43415                 });
43416             }
43417             
43418             
43419             tb.add(smenu);
43420             
43421             
43422         }
43423         
43424         var cmenu = { };
43425         if (!this.disable.cleanStyles) {
43426             cmenu = {
43427                 cls: 'x-btn-icon x-btn-clear',
43428                 
43429                 menu : {
43430                     items : []
43431                 }
43432             };
43433             for (var i =0; i < this.cleanStyles.length; i++) {
43434                 cmenu.menu.items.push({
43435                     actiontype : this.cleanStyles[i],
43436                     html: 'Remove ' + this.cleanStyles[i],
43437                     handler: function(a,b) {
43438                         Roo.log(a);
43439                         Roo.log(b);
43440                         var c = Roo.get(editorcore.doc.body);
43441                         c.select('[style]').each(function(s) {
43442                             s.dom.style.removeProperty(a.actiontype);
43443                         });
43444                         editorcore.syncValue();
43445                     },
43446                     tabIndex:-1
43447                 });
43448             }
43449             cmenu.menu.items.push({
43450                 actiontype : 'word',
43451                 html: 'Remove MS Word Formating',
43452                 handler: function(a,b) {
43453                     editorcore.cleanWord();
43454                     editorcore.syncValue();
43455                 },
43456                 tabIndex:-1
43457             });
43458             
43459             cmenu.menu.items.push({
43460                 actiontype : 'all',
43461                 html: 'Remove All Styles',
43462                 handler: function(a,b) {
43463                     
43464                     var c = Roo.get(editorcore.doc.body);
43465                     c.select('[style]').each(function(s) {
43466                         s.dom.removeAttribute('style');
43467                     });
43468                     editorcore.syncValue();
43469                 },
43470                 tabIndex:-1
43471             });
43472              cmenu.menu.items.push({
43473                 actiontype : 'word',
43474                 html: 'Tidy HTML Source',
43475                 handler: function(a,b) {
43476                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43477                     editorcore.syncValue();
43478                 },
43479                 tabIndex:-1
43480             });
43481             
43482             
43483             tb.add(cmenu);
43484         }
43485          
43486         if (!this.disable.specialElements) {
43487             var semenu = {
43488                 text: "Other;",
43489                 cls: 'x-edit-none',
43490                 menu : {
43491                     items : []
43492                 }
43493             };
43494             for (var i =0; i < this.specialElements.length; i++) {
43495                 semenu.menu.items.push(
43496                     Roo.apply({ 
43497                         handler: function(a,b) {
43498                             editor.insertAtCursor(this.ihtml);
43499                         }
43500                     }, this.specialElements[i])
43501                 );
43502                     
43503             }
43504             
43505             tb.add(semenu);
43506             
43507             
43508         }
43509          
43510         
43511         if (this.btns) {
43512             for(var i =0; i< this.btns.length;i++) {
43513                 var b = Roo.factory(this.btns[i],Roo.form);
43514                 b.cls =  'x-edit-none';
43515                 b.scope = editorcore;
43516                 tb.add(b);
43517             }
43518         
43519         }
43520         
43521         
43522         
43523         // disable everything...
43524         
43525         this.tb.items.each(function(item){
43526            if(item.id != editorcore.frameId+ '-sourceedit'){
43527                 item.disable();
43528             }
43529         });
43530         this.rendered = true;
43531         
43532         // the all the btns;
43533         editor.on('editorevent', this.updateToolbar, this);
43534         // other toolbars need to implement this..
43535         //editor.on('editmodechange', this.updateToolbar, this);
43536     },
43537     
43538     
43539     relayBtnCmd : function(btn) {
43540         this.editorcore.relayCmd(btn.cmd);
43541     },
43542     // private used internally
43543     createLink : function(){
43544         Roo.log("create link?");
43545         var url = prompt(this.createLinkText, this.defaultLinkValue);
43546         if(url && url != 'http:/'+'/'){
43547             this.editorcore.relayCmd('createlink', url);
43548         }
43549     },
43550
43551     
43552     /**
43553      * Protected method that will not generally be called directly. It triggers
43554      * a toolbar update by reading the markup state of the current selection in the editor.
43555      */
43556     updateToolbar: function(){
43557
43558         if(!this.editorcore.activated){
43559             this.editor.onFirstFocus();
43560             return;
43561         }
43562
43563         var btns = this.tb.items.map, 
43564             doc = this.editorcore.doc,
43565             frameId = this.editorcore.frameId;
43566
43567         if(!this.disable.font && !Roo.isSafari){
43568             /*
43569             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43570             if(name != this.fontSelect.dom.value){
43571                 this.fontSelect.dom.value = name;
43572             }
43573             */
43574         }
43575         if(!this.disable.format){
43576             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43577             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43578             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43579         }
43580         if(!this.disable.alignments){
43581             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43582             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43583             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43584         }
43585         if(!Roo.isSafari && !this.disable.lists){
43586             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43587             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43588         }
43589         
43590         var ans = this.editorcore.getAllAncestors();
43591         if (this.formatCombo) {
43592             
43593             
43594             var store = this.formatCombo.store;
43595             this.formatCombo.setValue("");
43596             for (var i =0; i < ans.length;i++) {
43597                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43598                     // select it..
43599                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43600                     break;
43601                 }
43602             }
43603         }
43604         
43605         
43606         
43607         // hides menus... - so this cant be on a menu...
43608         Roo.menu.MenuMgr.hideAll();
43609
43610         //this.editorsyncValue();
43611     },
43612    
43613     
43614     createFontOptions : function(){
43615         var buf = [], fs = this.fontFamilies, ff, lc;
43616         
43617         
43618         
43619         for(var i = 0, len = fs.length; i< len; i++){
43620             ff = fs[i];
43621             lc = ff.toLowerCase();
43622             buf.push(
43623                 '<option value="',lc,'" style="font-family:',ff,';"',
43624                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43625                     ff,
43626                 '</option>'
43627             );
43628         }
43629         return buf.join('');
43630     },
43631     
43632     toggleSourceEdit : function(sourceEditMode){
43633         
43634         Roo.log("toolbar toogle");
43635         if(sourceEditMode === undefined){
43636             sourceEditMode = !this.sourceEditMode;
43637         }
43638         this.sourceEditMode = sourceEditMode === true;
43639         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43640         // just toggle the button?
43641         if(btn.pressed !== this.sourceEditMode){
43642             btn.toggle(this.sourceEditMode);
43643             return;
43644         }
43645         
43646         if(sourceEditMode){
43647             Roo.log("disabling buttons");
43648             this.tb.items.each(function(item){
43649                 if(item.cmd != 'sourceedit'){
43650                     item.disable();
43651                 }
43652             });
43653           
43654         }else{
43655             Roo.log("enabling buttons");
43656             if(this.editorcore.initialized){
43657                 this.tb.items.each(function(item){
43658                     item.enable();
43659                 });
43660             }
43661             
43662         }
43663         Roo.log("calling toggole on editor");
43664         // tell the editor that it's been pressed..
43665         this.editor.toggleSourceEdit(sourceEditMode);
43666        
43667     },
43668      /**
43669      * Object collection of toolbar tooltips for the buttons in the editor. The key
43670      * is the command id associated with that button and the value is a valid QuickTips object.
43671      * For example:
43672 <pre><code>
43673 {
43674     bold : {
43675         title: 'Bold (Ctrl+B)',
43676         text: 'Make the selected text bold.',
43677         cls: 'x-html-editor-tip'
43678     },
43679     italic : {
43680         title: 'Italic (Ctrl+I)',
43681         text: 'Make the selected text italic.',
43682         cls: 'x-html-editor-tip'
43683     },
43684     ...
43685 </code></pre>
43686     * @type Object
43687      */
43688     buttonTips : {
43689         bold : {
43690             title: 'Bold (Ctrl+B)',
43691             text: 'Make the selected text bold.',
43692             cls: 'x-html-editor-tip'
43693         },
43694         italic : {
43695             title: 'Italic (Ctrl+I)',
43696             text: 'Make the selected text italic.',
43697             cls: 'x-html-editor-tip'
43698         },
43699         underline : {
43700             title: 'Underline (Ctrl+U)',
43701             text: 'Underline the selected text.',
43702             cls: 'x-html-editor-tip'
43703         },
43704         increasefontsize : {
43705             title: 'Grow Text',
43706             text: 'Increase the font size.',
43707             cls: 'x-html-editor-tip'
43708         },
43709         decreasefontsize : {
43710             title: 'Shrink Text',
43711             text: 'Decrease the font size.',
43712             cls: 'x-html-editor-tip'
43713         },
43714         backcolor : {
43715             title: 'Text Highlight Color',
43716             text: 'Change the background color of the selected text.',
43717             cls: 'x-html-editor-tip'
43718         },
43719         forecolor : {
43720             title: 'Font Color',
43721             text: 'Change the color of the selected text.',
43722             cls: 'x-html-editor-tip'
43723         },
43724         justifyleft : {
43725             title: 'Align Text Left',
43726             text: 'Align text to the left.',
43727             cls: 'x-html-editor-tip'
43728         },
43729         justifycenter : {
43730             title: 'Center Text',
43731             text: 'Center text in the editor.',
43732             cls: 'x-html-editor-tip'
43733         },
43734         justifyright : {
43735             title: 'Align Text Right',
43736             text: 'Align text to the right.',
43737             cls: 'x-html-editor-tip'
43738         },
43739         insertunorderedlist : {
43740             title: 'Bullet List',
43741             text: 'Start a bulleted list.',
43742             cls: 'x-html-editor-tip'
43743         },
43744         insertorderedlist : {
43745             title: 'Numbered List',
43746             text: 'Start a numbered list.',
43747             cls: 'x-html-editor-tip'
43748         },
43749         createlink : {
43750             title: 'Hyperlink',
43751             text: 'Make the selected text a hyperlink.',
43752             cls: 'x-html-editor-tip'
43753         },
43754         sourceedit : {
43755             title: 'Source Edit',
43756             text: 'Switch to source editing mode.',
43757             cls: 'x-html-editor-tip'
43758         }
43759     },
43760     // private
43761     onDestroy : function(){
43762         if(this.rendered){
43763             
43764             this.tb.items.each(function(item){
43765                 if(item.menu){
43766                     item.menu.removeAll();
43767                     if(item.menu.el){
43768                         item.menu.el.destroy();
43769                     }
43770                 }
43771                 item.destroy();
43772             });
43773              
43774         }
43775     },
43776     onFirstFocus: function() {
43777         this.tb.items.each(function(item){
43778            item.enable();
43779         });
43780     }
43781 });
43782
43783
43784
43785
43786 // <script type="text/javascript">
43787 /*
43788  * Based on
43789  * Ext JS Library 1.1.1
43790  * Copyright(c) 2006-2007, Ext JS, LLC.
43791  *  
43792  
43793  */
43794
43795  
43796 /**
43797  * @class Roo.form.HtmlEditor.ToolbarContext
43798  * Context Toolbar
43799  * 
43800  * Usage:
43801  *
43802  new Roo.form.HtmlEditor({
43803     ....
43804     toolbars : [
43805         { xtype: 'ToolbarStandard', styles : {} }
43806         { xtype: 'ToolbarContext', disable : {} }
43807     ]
43808 })
43809
43810      
43811  * 
43812  * @config : {Object} disable List of elements to disable.. (not done yet.)
43813  * @config : {Object} styles  Map of styles available.
43814  * 
43815  */
43816
43817 Roo.form.HtmlEditor.ToolbarContext = function(config)
43818 {
43819     
43820     Roo.apply(this, config);
43821     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43822     // dont call parent... till later.
43823     this.styles = this.styles || {};
43824 }
43825
43826  
43827
43828 Roo.form.HtmlEditor.ToolbarContext.types = {
43829     'IMG' : {
43830         width : {
43831             title: "Width",
43832             width: 40
43833         },
43834         height:  {
43835             title: "Height",
43836             width: 40
43837         },
43838         align: {
43839             title: "Align",
43840             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43841             width : 80
43842             
43843         },
43844         border: {
43845             title: "Border",
43846             width: 40
43847         },
43848         alt: {
43849             title: "Alt",
43850             width: 120
43851         },
43852         src : {
43853             title: "Src",
43854             width: 220
43855         }
43856         
43857     },
43858     'A' : {
43859         name : {
43860             title: "Name",
43861             width: 50
43862         },
43863         target:  {
43864             title: "Target",
43865             width: 120
43866         },
43867         href:  {
43868             title: "Href",
43869             width: 220
43870         } // border?
43871         
43872     },
43873     'TABLE' : {
43874         rows : {
43875             title: "Rows",
43876             width: 20
43877         },
43878         cols : {
43879             title: "Cols",
43880             width: 20
43881         },
43882         width : {
43883             title: "Width",
43884             width: 40
43885         },
43886         height : {
43887             title: "Height",
43888             width: 40
43889         },
43890         border : {
43891             title: "Border",
43892             width: 20
43893         }
43894     },
43895     'TD' : {
43896         width : {
43897             title: "Width",
43898             width: 40
43899         },
43900         height : {
43901             title: "Height",
43902             width: 40
43903         },   
43904         align: {
43905             title: "Align",
43906             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43907             width: 80
43908         },
43909         valign: {
43910             title: "Valign",
43911             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43912             width: 80
43913         },
43914         colspan: {
43915             title: "Colspan",
43916             width: 20
43917             
43918         },
43919          'font-family'  : {
43920             title : "Font",
43921             style : 'fontFamily',
43922             displayField: 'display',
43923             optname : 'font-family',
43924             width: 140
43925         }
43926     },
43927     'INPUT' : {
43928         name : {
43929             title: "name",
43930             width: 120
43931         },
43932         value : {
43933             title: "Value",
43934             width: 120
43935         },
43936         width : {
43937             title: "Width",
43938             width: 40
43939         }
43940     },
43941     'LABEL' : {
43942         'for' : {
43943             title: "For",
43944             width: 120
43945         }
43946     },
43947     'TEXTAREA' : {
43948           name : {
43949             title: "name",
43950             width: 120
43951         },
43952         rows : {
43953             title: "Rows",
43954             width: 20
43955         },
43956         cols : {
43957             title: "Cols",
43958             width: 20
43959         }
43960     },
43961     'SELECT' : {
43962         name : {
43963             title: "name",
43964             width: 120
43965         },
43966         selectoptions : {
43967             title: "Options",
43968             width: 200
43969         }
43970     },
43971     
43972     // should we really allow this??
43973     // should this just be 
43974     'BODY' : {
43975         title : {
43976             title: "Title",
43977             width: 200,
43978             disabled : true
43979         }
43980     },
43981     'SPAN' : {
43982         'font-family'  : {
43983             title : "Font",
43984             style : 'fontFamily',
43985             displayField: 'display',
43986             optname : 'font-family',
43987             width: 140
43988         }
43989     },
43990     'DIV' : {
43991         'font-family'  : {
43992             title : "Font",
43993             style : 'fontFamily',
43994             displayField: 'display',
43995             optname : 'font-family',
43996             width: 140
43997         }
43998     },
43999      'P' : {
44000         'font-family'  : {
44001             title : "Font",
44002             style : 'fontFamily',
44003             displayField: 'display',
44004             optname : 'font-family',
44005             width: 140
44006         }
44007     },
44008     
44009     '*' : {
44010         // empty..
44011     }
44012
44013 };
44014
44015 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44016 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44017
44018 Roo.form.HtmlEditor.ToolbarContext.options = {
44019         'font-family'  : [ 
44020                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44021                 [ 'Courier New', 'Courier New'],
44022                 [ 'Tahoma', 'Tahoma'],
44023                 [ 'Times New Roman,serif', 'Times'],
44024                 [ 'Verdana','Verdana' ]
44025         ]
44026 };
44027
44028 // fixme - these need to be configurable..
44029  
44030
44031 Roo.form.HtmlEditor.ToolbarContext.types
44032
44033
44034 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44035     
44036     tb: false,
44037     
44038     rendered: false,
44039     
44040     editor : false,
44041     editorcore : false,
44042     /**
44043      * @cfg {Object} disable  List of toolbar elements to disable
44044          
44045      */
44046     disable : false,
44047     /**
44048      * @cfg {Object} styles List of styles 
44049      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44050      *
44051      * These must be defined in the page, so they get rendered correctly..
44052      * .headline { }
44053      * TD.underline { }
44054      * 
44055      */
44056     styles : false,
44057     
44058     options: false,
44059     
44060     toolbars : false,
44061     
44062     init : function(editor)
44063     {
44064         this.editor = editor;
44065         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44066         var editorcore = this.editorcore;
44067         
44068         var fid = editorcore.frameId;
44069         var etb = this;
44070         function btn(id, toggle, handler){
44071             var xid = fid + '-'+ id ;
44072             return {
44073                 id : xid,
44074                 cmd : id,
44075                 cls : 'x-btn-icon x-edit-'+id,
44076                 enableToggle:toggle !== false,
44077                 scope: editorcore, // was editor...
44078                 handler:handler||editorcore.relayBtnCmd,
44079                 clickEvent:'mousedown',
44080                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44081                 tabIndex:-1
44082             };
44083         }
44084         // create a new element.
44085         var wdiv = editor.wrap.createChild({
44086                 tag: 'div'
44087             }, editor.wrap.dom.firstChild.nextSibling, true);
44088         
44089         // can we do this more than once??
44090         
44091          // stop form submits
44092       
44093  
44094         // disable everything...
44095         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44096         this.toolbars = {};
44097            
44098         for (var i in  ty) {
44099           
44100             this.toolbars[i] = this.buildToolbar(ty[i],i);
44101         }
44102         this.tb = this.toolbars.BODY;
44103         this.tb.el.show();
44104         this.buildFooter();
44105         this.footer.show();
44106         editor.on('hide', function( ) { this.footer.hide() }, this);
44107         editor.on('show', function( ) { this.footer.show() }, this);
44108         
44109          
44110         this.rendered = true;
44111         
44112         // the all the btns;
44113         editor.on('editorevent', this.updateToolbar, this);
44114         // other toolbars need to implement this..
44115         //editor.on('editmodechange', this.updateToolbar, this);
44116     },
44117     
44118     
44119     
44120     /**
44121      * Protected method that will not generally be called directly. It triggers
44122      * a toolbar update by reading the markup state of the current selection in the editor.
44123      */
44124     updateToolbar: function(editor,ev,sel){
44125
44126         //Roo.log(ev);
44127         // capture mouse up - this is handy for selecting images..
44128         // perhaps should go somewhere else...
44129         if(!this.editorcore.activated){
44130              this.editor.onFirstFocus();
44131             return;
44132         }
44133         
44134         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44135         // selectNode - might want to handle IE?
44136         if (ev &&
44137             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44138             ev.target && ev.target.tagName == 'IMG') {
44139             // they have click on an image...
44140             // let's see if we can change the selection...
44141             sel = ev.target;
44142          
44143               var nodeRange = sel.ownerDocument.createRange();
44144             try {
44145                 nodeRange.selectNode(sel);
44146             } catch (e) {
44147                 nodeRange.selectNodeContents(sel);
44148             }
44149             //nodeRange.collapse(true);
44150             var s = this.editorcore.win.getSelection();
44151             s.removeAllRanges();
44152             s.addRange(nodeRange);
44153         }  
44154         
44155       
44156         var updateFooter = sel ? false : true;
44157         
44158         
44159         var ans = this.editorcore.getAllAncestors();
44160         
44161         // pick
44162         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44163         
44164         if (!sel) { 
44165             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44166             sel = sel ? sel : this.editorcore.doc.body;
44167             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44168             
44169         }
44170         // pick a menu that exists..
44171         var tn = sel.tagName.toUpperCase();
44172         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44173         
44174         tn = sel.tagName.toUpperCase();
44175         
44176         var lastSel = this.tb.selectedNode
44177         
44178         this.tb.selectedNode = sel;
44179         
44180         // if current menu does not match..
44181         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44182                 
44183             this.tb.el.hide();
44184             ///console.log("show: " + tn);
44185             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44186             this.tb.el.show();
44187             // update name
44188             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44189             
44190             
44191             // update attributes
44192             if (this.tb.fields) {
44193                 this.tb.fields.each(function(e) {
44194                     if (e.stylename) {
44195                         e.setValue(sel.style[e.stylename]);
44196                         return;
44197                     } 
44198                    e.setValue(sel.getAttribute(e.attrname));
44199                 });
44200             }
44201             
44202             var hasStyles = false;
44203             for(var i in this.styles) {
44204                 hasStyles = true;
44205                 break;
44206             }
44207             
44208             // update styles
44209             if (hasStyles) { 
44210                 var st = this.tb.fields.item(0);
44211                 
44212                 st.store.removeAll();
44213                
44214                 
44215                 var cn = sel.className.split(/\s+/);
44216                 
44217                 var avs = [];
44218                 if (this.styles['*']) {
44219                     
44220                     Roo.each(this.styles['*'], function(v) {
44221                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44222                     });
44223                 }
44224                 if (this.styles[tn]) { 
44225                     Roo.each(this.styles[tn], function(v) {
44226                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44227                     });
44228                 }
44229                 
44230                 st.store.loadData(avs);
44231                 st.collapse();
44232                 st.setValue(cn);
44233             }
44234             // flag our selected Node.
44235             this.tb.selectedNode = sel;
44236            
44237            
44238             Roo.menu.MenuMgr.hideAll();
44239
44240         }
44241         
44242         if (!updateFooter) {
44243             //this.footDisp.dom.innerHTML = ''; 
44244             return;
44245         }
44246         // update the footer
44247         //
44248         var html = '';
44249         
44250         this.footerEls = ans.reverse();
44251         Roo.each(this.footerEls, function(a,i) {
44252             if (!a) { return; }
44253             html += html.length ? ' &gt; '  :  '';
44254             
44255             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44256             
44257         });
44258        
44259         // 
44260         var sz = this.footDisp.up('td').getSize();
44261         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44262         this.footDisp.dom.style.marginLeft = '5px';
44263         
44264         this.footDisp.dom.style.overflow = 'hidden';
44265         
44266         this.footDisp.dom.innerHTML = html;
44267             
44268         //this.editorsyncValue();
44269     },
44270      
44271     
44272    
44273        
44274     // private
44275     onDestroy : function(){
44276         if(this.rendered){
44277             
44278             this.tb.items.each(function(item){
44279                 if(item.menu){
44280                     item.menu.removeAll();
44281                     if(item.menu.el){
44282                         item.menu.el.destroy();
44283                     }
44284                 }
44285                 item.destroy();
44286             });
44287              
44288         }
44289     },
44290     onFirstFocus: function() {
44291         // need to do this for all the toolbars..
44292         this.tb.items.each(function(item){
44293            item.enable();
44294         });
44295     },
44296     buildToolbar: function(tlist, nm)
44297     {
44298         var editor = this.editor;
44299         var editorcore = this.editorcore;
44300          // create a new element.
44301         var wdiv = editor.wrap.createChild({
44302                 tag: 'div'
44303             }, editor.wrap.dom.firstChild.nextSibling, true);
44304         
44305        
44306         var tb = new Roo.Toolbar(wdiv);
44307         // add the name..
44308         
44309         tb.add(nm+ ":&nbsp;");
44310         
44311         var styles = [];
44312         for(var i in this.styles) {
44313             styles.push(i);
44314         }
44315         
44316         // styles...
44317         if (styles && styles.length) {
44318             
44319             // this needs a multi-select checkbox...
44320             tb.addField( new Roo.form.ComboBox({
44321                 store: new Roo.data.SimpleStore({
44322                     id : 'val',
44323                     fields: ['val', 'selected'],
44324                     data : [] 
44325                 }),
44326                 name : '-roo-edit-className',
44327                 attrname : 'className',
44328                 displayField: 'val',
44329                 typeAhead: false,
44330                 mode: 'local',
44331                 editable : false,
44332                 triggerAction: 'all',
44333                 emptyText:'Select Style',
44334                 selectOnFocus:true,
44335                 width: 130,
44336                 listeners : {
44337                     'select': function(c, r, i) {
44338                         // initial support only for on class per el..
44339                         tb.selectedNode.className =  r ? r.get('val') : '';
44340                         editorcore.syncValue();
44341                     }
44342                 }
44343     
44344             }));
44345         }
44346         
44347         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44348         var tbops = tbc.options;
44349         
44350         for (var i in tlist) {
44351             
44352             var item = tlist[i];
44353             tb.add(item.title + ":&nbsp;");
44354             
44355             
44356             //optname == used so you can configure the options available..
44357             var opts = item.opts ? item.opts : false;
44358             if (item.optname) {
44359                 opts = tbops[item.optname];
44360            
44361             }
44362             
44363             if (opts) {
44364                 // opts == pulldown..
44365                 tb.addField( new Roo.form.ComboBox({
44366                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44367                         id : 'val',
44368                         fields: ['val', 'display'],
44369                         data : opts  
44370                     }),
44371                     name : '-roo-edit-' + i,
44372                     attrname : i,
44373                     stylename : item.style ? item.style : false,
44374                     displayField: item.displayField ? item.displayField : 'val',
44375                     valueField :  'val',
44376                     typeAhead: false,
44377                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44378                     editable : false,
44379                     triggerAction: 'all',
44380                     emptyText:'Select',
44381                     selectOnFocus:true,
44382                     width: item.width ? item.width  : 130,
44383                     listeners : {
44384                         'select': function(c, r, i) {
44385                             if (c.stylename) {
44386                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44387                                 return;
44388                             }
44389                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44390                         }
44391                     }
44392
44393                 }));
44394                 continue;
44395                     
44396                  
44397                 
44398                 tb.addField( new Roo.form.TextField({
44399                     name: i,
44400                     width: 100,
44401                     //allowBlank:false,
44402                     value: ''
44403                 }));
44404                 continue;
44405             }
44406             tb.addField( new Roo.form.TextField({
44407                 name: '-roo-edit-' + i,
44408                 attrname : i,
44409                 
44410                 width: item.width,
44411                 //allowBlank:true,
44412                 value: '',
44413                 listeners: {
44414                     'change' : function(f, nv, ov) {
44415                         tb.selectedNode.setAttribute(f.attrname, nv);
44416                     }
44417                 }
44418             }));
44419              
44420         }
44421         tb.addFill();
44422         var _this = this;
44423         tb.addButton( {
44424             text: 'Remove Tag',
44425     
44426             listeners : {
44427                 click : function ()
44428                 {
44429                     // remove
44430                     // undo does not work.
44431                      
44432                     var sn = tb.selectedNode;
44433                     
44434                     var pn = sn.parentNode;
44435                     
44436                     var stn =  sn.childNodes[0];
44437                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44438                     while (sn.childNodes.length) {
44439                         var node = sn.childNodes[0];
44440                         sn.removeChild(node);
44441                         //Roo.log(node);
44442                         pn.insertBefore(node, sn);
44443                         
44444                     }
44445                     pn.removeChild(sn);
44446                     var range = editorcore.createRange();
44447         
44448                     range.setStart(stn,0);
44449                     range.setEnd(en,0); //????
44450                     //range.selectNode(sel);
44451                     
44452                     
44453                     var selection = editorcore.getSelection();
44454                     selection.removeAllRanges();
44455                     selection.addRange(range);
44456                     
44457                     
44458                     
44459                     //_this.updateToolbar(null, null, pn);
44460                     _this.updateToolbar(null, null, null);
44461                     _this.footDisp.dom.innerHTML = ''; 
44462                 }
44463             }
44464             
44465                     
44466                 
44467             
44468         });
44469         
44470         
44471         tb.el.on('click', function(e){
44472             e.preventDefault(); // what does this do?
44473         });
44474         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44475         tb.el.hide();
44476         tb.name = nm;
44477         // dont need to disable them... as they will get hidden
44478         return tb;
44479          
44480         
44481     },
44482     buildFooter : function()
44483     {
44484         
44485         var fel = this.editor.wrap.createChild();
44486         this.footer = new Roo.Toolbar(fel);
44487         // toolbar has scrolly on left / right?
44488         var footDisp= new Roo.Toolbar.Fill();
44489         var _t = this;
44490         this.footer.add(
44491             {
44492                 text : '&lt;',
44493                 xtype: 'Button',
44494                 handler : function() {
44495                     _t.footDisp.scrollTo('left',0,true)
44496                 }
44497             }
44498         );
44499         this.footer.add( footDisp );
44500         this.footer.add( 
44501             {
44502                 text : '&gt;',
44503                 xtype: 'Button',
44504                 handler : function() {
44505                     // no animation..
44506                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44507                 }
44508             }
44509         );
44510         var fel = Roo.get(footDisp.el);
44511         fel.addClass('x-editor-context');
44512         this.footDispWrap = fel; 
44513         this.footDispWrap.overflow  = 'hidden';
44514         
44515         this.footDisp = fel.createChild();
44516         this.footDispWrap.on('click', this.onContextClick, this)
44517         
44518         
44519     },
44520     onContextClick : function (ev,dom)
44521     {
44522         ev.preventDefault();
44523         var  cn = dom.className;
44524         //Roo.log(cn);
44525         if (!cn.match(/x-ed-loc-/)) {
44526             return;
44527         }
44528         var n = cn.split('-').pop();
44529         var ans = this.footerEls;
44530         var sel = ans[n];
44531         
44532          // pick
44533         var range = this.editorcore.createRange();
44534         
44535         range.selectNodeContents(sel);
44536         //range.selectNode(sel);
44537         
44538         
44539         var selection = this.editorcore.getSelection();
44540         selection.removeAllRanges();
44541         selection.addRange(range);
44542         
44543         
44544         
44545         this.updateToolbar(null, null, sel);
44546         
44547         
44548     }
44549     
44550     
44551     
44552     
44553     
44554 });
44555
44556
44557
44558
44559
44560 /*
44561  * Based on:
44562  * Ext JS Library 1.1.1
44563  * Copyright(c) 2006-2007, Ext JS, LLC.
44564  *
44565  * Originally Released Under LGPL - original licence link has changed is not relivant.
44566  *
44567  * Fork - LGPL
44568  * <script type="text/javascript">
44569  */
44570  
44571 /**
44572  * @class Roo.form.BasicForm
44573  * @extends Roo.util.Observable
44574  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44575  * @constructor
44576  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44577  * @param {Object} config Configuration options
44578  */
44579 Roo.form.BasicForm = function(el, config){
44580     this.allItems = [];
44581     this.childForms = [];
44582     Roo.apply(this, config);
44583     /*
44584      * The Roo.form.Field items in this form.
44585      * @type MixedCollection
44586      */
44587      
44588      
44589     this.items = new Roo.util.MixedCollection(false, function(o){
44590         return o.id || (o.id = Roo.id());
44591     });
44592     this.addEvents({
44593         /**
44594          * @event beforeaction
44595          * Fires before any action is performed. Return false to cancel the action.
44596          * @param {Form} this
44597          * @param {Action} action The action to be performed
44598          */
44599         beforeaction: true,
44600         /**
44601          * @event actionfailed
44602          * Fires when an action fails.
44603          * @param {Form} this
44604          * @param {Action} action The action that failed
44605          */
44606         actionfailed : true,
44607         /**
44608          * @event actioncomplete
44609          * Fires when an action is completed.
44610          * @param {Form} this
44611          * @param {Action} action The action that completed
44612          */
44613         actioncomplete : true
44614     });
44615     if(el){
44616         this.initEl(el);
44617     }
44618     Roo.form.BasicForm.superclass.constructor.call(this);
44619 };
44620
44621 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44622     /**
44623      * @cfg {String} method
44624      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44625      */
44626     /**
44627      * @cfg {DataReader} reader
44628      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44629      * This is optional as there is built-in support for processing JSON.
44630      */
44631     /**
44632      * @cfg {DataReader} errorReader
44633      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44634      * This is completely optional as there is built-in support for processing JSON.
44635      */
44636     /**
44637      * @cfg {String} url
44638      * The URL to use for form actions if one isn't supplied in the action options.
44639      */
44640     /**
44641      * @cfg {Boolean} fileUpload
44642      * Set to true if this form is a file upload.
44643      */
44644      
44645     /**
44646      * @cfg {Object} baseParams
44647      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44648      */
44649      /**
44650      
44651     /**
44652      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44653      */
44654     timeout: 30,
44655
44656     // private
44657     activeAction : null,
44658
44659     /**
44660      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44661      * or setValues() data instead of when the form was first created.
44662      */
44663     trackResetOnLoad : false,
44664     
44665     
44666     /**
44667      * childForms - used for multi-tab forms
44668      * @type {Array}
44669      */
44670     childForms : false,
44671     
44672     /**
44673      * allItems - full list of fields.
44674      * @type {Array}
44675      */
44676     allItems : false,
44677     
44678     /**
44679      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44680      * element by passing it or its id or mask the form itself by passing in true.
44681      * @type Mixed
44682      */
44683     waitMsgTarget : false,
44684
44685     // private
44686     initEl : function(el){
44687         this.el = Roo.get(el);
44688         this.id = this.el.id || Roo.id();
44689         this.el.on('submit', this.onSubmit, this);
44690         this.el.addClass('x-form');
44691     },
44692
44693     // private
44694     onSubmit : function(e){
44695         e.stopEvent();
44696     },
44697
44698     /**
44699      * Returns true if client-side validation on the form is successful.
44700      * @return Boolean
44701      */
44702     isValid : function(){
44703         var valid = true;
44704         this.items.each(function(f){
44705            if(!f.validate()){
44706                valid = false;
44707            }
44708         });
44709         return valid;
44710     },
44711
44712     /**
44713      * Returns true if any fields in this form have changed since their original load.
44714      * @return Boolean
44715      */
44716     isDirty : function(){
44717         var dirty = false;
44718         this.items.each(function(f){
44719            if(f.isDirty()){
44720                dirty = true;
44721                return false;
44722            }
44723         });
44724         return dirty;
44725     },
44726
44727     /**
44728      * Performs a predefined action (submit or load) or custom actions you define on this form.
44729      * @param {String} actionName The name of the action type
44730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44732      * accept other config options):
44733      * <pre>
44734 Property          Type             Description
44735 ----------------  ---------------  ----------------------------------------------------------------------------------
44736 url               String           The url for the action (defaults to the form's url)
44737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44740                                    validate the form on the client (defaults to false)
44741      * </pre>
44742      * @return {BasicForm} this
44743      */
44744     doAction : function(action, options){
44745         if(typeof action == 'string'){
44746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44747         }
44748         if(this.fireEvent('beforeaction', this, action) !== false){
44749             this.beforeAction(action);
44750             action.run.defer(100, action);
44751         }
44752         return this;
44753     },
44754
44755     /**
44756      * Shortcut to do a submit action.
44757      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44758      * @return {BasicForm} this
44759      */
44760     submit : function(options){
44761         this.doAction('submit', options);
44762         return this;
44763     },
44764
44765     /**
44766      * Shortcut to do a load action.
44767      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44768      * @return {BasicForm} this
44769      */
44770     load : function(options){
44771         this.doAction('load', options);
44772         return this;
44773     },
44774
44775     /**
44776      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44777      * @param {Record} record The record to edit
44778      * @return {BasicForm} this
44779      */
44780     updateRecord : function(record){
44781         record.beginEdit();
44782         var fs = record.fields;
44783         fs.each(function(f){
44784             var field = this.findField(f.name);
44785             if(field){
44786                 record.set(f.name, field.getValue());
44787             }
44788         }, this);
44789         record.endEdit();
44790         return this;
44791     },
44792
44793     /**
44794      * Loads an Roo.data.Record into this form.
44795      * @param {Record} record The record to load
44796      * @return {BasicForm} this
44797      */
44798     loadRecord : function(record){
44799         this.setValues(record.data);
44800         return this;
44801     },
44802
44803     // private
44804     beforeAction : function(action){
44805         var o = action.options;
44806         
44807        
44808         if(this.waitMsgTarget === true){
44809             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44810         }else if(this.waitMsgTarget){
44811             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44812             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44813         }else {
44814             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44815         }
44816          
44817     },
44818
44819     // private
44820     afterAction : function(action, success){
44821         this.activeAction = null;
44822         var o = action.options;
44823         
44824         if(this.waitMsgTarget === true){
44825             this.el.unmask();
44826         }else if(this.waitMsgTarget){
44827             this.waitMsgTarget.unmask();
44828         }else{
44829             Roo.MessageBox.updateProgress(1);
44830             Roo.MessageBox.hide();
44831         }
44832          
44833         if(success){
44834             if(o.reset){
44835                 this.reset();
44836             }
44837             Roo.callback(o.success, o.scope, [this, action]);
44838             this.fireEvent('actioncomplete', this, action);
44839             
44840         }else{
44841             
44842             // failure condition..
44843             // we have a scenario where updates need confirming.
44844             // eg. if a locking scenario exists..
44845             // we look for { errors : { needs_confirm : true }} in the response.
44846             if (
44847                 (typeof(action.result) != 'undefined')  &&
44848                 (typeof(action.result.errors) != 'undefined')  &&
44849                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44850            ){
44851                 var _t = this;
44852                 Roo.MessageBox.confirm(
44853                     "Change requires confirmation",
44854                     action.result.errorMsg,
44855                     function(r) {
44856                         if (r != 'yes') {
44857                             return;
44858                         }
44859                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44860                     }
44861                     
44862                 );
44863                 
44864                 
44865                 
44866                 return;
44867             }
44868             
44869             Roo.callback(o.failure, o.scope, [this, action]);
44870             // show an error message if no failed handler is set..
44871             if (!this.hasListener('actionfailed')) {
44872                 Roo.MessageBox.alert("Error",
44873                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44874                         action.result.errorMsg :
44875                         "Saving Failed, please check your entries or try again"
44876                 );
44877             }
44878             
44879             this.fireEvent('actionfailed', this, action);
44880         }
44881         
44882     },
44883
44884     /**
44885      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44886      * @param {String} id The value to search for
44887      * @return Field
44888      */
44889     findField : function(id){
44890         var field = this.items.get(id);
44891         if(!field){
44892             this.items.each(function(f){
44893                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44894                     field = f;
44895                     return false;
44896                 }
44897             });
44898         }
44899         return field || null;
44900     },
44901
44902     /**
44903      * Add a secondary form to this one, 
44904      * Used to provide tabbed forms. One form is primary, with hidden values 
44905      * which mirror the elements from the other forms.
44906      * 
44907      * @param {Roo.form.Form} form to add.
44908      * 
44909      */
44910     addForm : function(form)
44911     {
44912        
44913         if (this.childForms.indexOf(form) > -1) {
44914             // already added..
44915             return;
44916         }
44917         this.childForms.push(form);
44918         var n = '';
44919         Roo.each(form.allItems, function (fe) {
44920             
44921             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44922             if (this.findField(n)) { // already added..
44923                 return;
44924             }
44925             var add = new Roo.form.Hidden({
44926                 name : n
44927             });
44928             add.render(this.el);
44929             
44930             this.add( add );
44931         }, this);
44932         
44933     },
44934     /**
44935      * Mark fields in this form invalid in bulk.
44936      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44937      * @return {BasicForm} this
44938      */
44939     markInvalid : function(errors){
44940         if(errors instanceof Array){
44941             for(var i = 0, len = errors.length; i < len; i++){
44942                 var fieldError = errors[i];
44943                 var f = this.findField(fieldError.id);
44944                 if(f){
44945                     f.markInvalid(fieldError.msg);
44946                 }
44947             }
44948         }else{
44949             var field, id;
44950             for(id in errors){
44951                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44952                     field.markInvalid(errors[id]);
44953                 }
44954             }
44955         }
44956         Roo.each(this.childForms || [], function (f) {
44957             f.markInvalid(errors);
44958         });
44959         
44960         return this;
44961     },
44962
44963     /**
44964      * Set values for fields in this form in bulk.
44965      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44966      * @return {BasicForm} this
44967      */
44968     setValues : function(values){
44969         if(values instanceof Array){ // array of objects
44970             for(var i = 0, len = values.length; i < len; i++){
44971                 var v = values[i];
44972                 var f = this.findField(v.id);
44973                 if(f){
44974                     f.setValue(v.value);
44975                     if(this.trackResetOnLoad){
44976                         f.originalValue = f.getValue();
44977                     }
44978                 }
44979             }
44980         }else{ // object hash
44981             var field, id;
44982             for(id in values){
44983                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44984                     
44985                     if (field.setFromData && 
44986                         field.valueField && 
44987                         field.displayField &&
44988                         // combos' with local stores can 
44989                         // be queried via setValue()
44990                         // to set their value..
44991                         (field.store && !field.store.isLocal)
44992                         ) {
44993                         // it's a combo
44994                         var sd = { };
44995                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44996                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44997                         field.setFromData(sd);
44998                         
44999                     } else {
45000                         field.setValue(values[id]);
45001                     }
45002                     
45003                     
45004                     if(this.trackResetOnLoad){
45005                         field.originalValue = field.getValue();
45006                     }
45007                 }
45008             }
45009         }
45010          
45011         Roo.each(this.childForms || [], function (f) {
45012             f.setValues(values);
45013         });
45014                 
45015         return this;
45016     },
45017
45018     /**
45019      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45020      * they are returned as an array.
45021      * @param {Boolean} asString
45022      * @return {Object}
45023      */
45024     getValues : function(asString){
45025         if (this.childForms) {
45026             // copy values from the child forms
45027             Roo.each(this.childForms, function (f) {
45028                 this.setValues(f.getValues());
45029             }, this);
45030         }
45031         
45032         
45033         
45034         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45035         if(asString === true){
45036             return fs;
45037         }
45038         return Roo.urlDecode(fs);
45039     },
45040     
45041     /**
45042      * Returns the fields in this form as an object with key/value pairs. 
45043      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45044      * @return {Object}
45045      */
45046     getFieldValues : function(with_hidden)
45047     {
45048         if (this.childForms) {
45049             // copy values from the child forms
45050             // should this call getFieldValues - probably not as we do not currently copy
45051             // hidden fields when we generate..
45052             Roo.each(this.childForms, function (f) {
45053                 this.setValues(f.getValues());
45054             }, this);
45055         }
45056         
45057         var ret = {};
45058         this.items.each(function(f){
45059             if (!f.getName()) {
45060                 return;
45061             }
45062             var v = f.getValue();
45063             if (f.inputType =='radio') {
45064                 if (typeof(ret[f.getName()]) == 'undefined') {
45065                     ret[f.getName()] = ''; // empty..
45066                 }
45067                 
45068                 if (!f.el.dom.checked) {
45069                     return;
45070                     
45071                 }
45072                 v = f.el.dom.value;
45073                 
45074             }
45075             
45076             // not sure if this supported any more..
45077             if ((typeof(v) == 'object') && f.getRawValue) {
45078                 v = f.getRawValue() ; // dates..
45079             }
45080             // combo boxes where name != hiddenName...
45081             if (f.name != f.getName()) {
45082                 ret[f.name] = f.getRawValue();
45083             }
45084             ret[f.getName()] = v;
45085         });
45086         
45087         return ret;
45088     },
45089
45090     /**
45091      * Clears all invalid messages in this form.
45092      * @return {BasicForm} this
45093      */
45094     clearInvalid : function(){
45095         this.items.each(function(f){
45096            f.clearInvalid();
45097         });
45098         
45099         Roo.each(this.childForms || [], function (f) {
45100             f.clearInvalid();
45101         });
45102         
45103         
45104         return this;
45105     },
45106
45107     /**
45108      * Resets this form.
45109      * @return {BasicForm} this
45110      */
45111     reset : function(){
45112         this.items.each(function(f){
45113             f.reset();
45114         });
45115         
45116         Roo.each(this.childForms || [], function (f) {
45117             f.reset();
45118         });
45119        
45120         
45121         return this;
45122     },
45123
45124     /**
45125      * Add Roo.form components to this form.
45126      * @param {Field} field1
45127      * @param {Field} field2 (optional)
45128      * @param {Field} etc (optional)
45129      * @return {BasicForm} this
45130      */
45131     add : function(){
45132         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45133         return this;
45134     },
45135
45136
45137     /**
45138      * Removes a field from the items collection (does NOT remove its markup).
45139      * @param {Field} field
45140      * @return {BasicForm} this
45141      */
45142     remove : function(field){
45143         this.items.remove(field);
45144         return this;
45145     },
45146
45147     /**
45148      * Looks at the fields in this form, checks them for an id attribute,
45149      * and calls applyTo on the existing dom element with that id.
45150      * @return {BasicForm} this
45151      */
45152     render : function(){
45153         this.items.each(function(f){
45154             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45155                 f.applyTo(f.id);
45156             }
45157         });
45158         return this;
45159     },
45160
45161     /**
45162      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45163      * @param {Object} values
45164      * @return {BasicForm} this
45165      */
45166     applyToFields : function(o){
45167         this.items.each(function(f){
45168            Roo.apply(f, o);
45169         });
45170         return this;
45171     },
45172
45173     /**
45174      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45175      * @param {Object} values
45176      * @return {BasicForm} this
45177      */
45178     applyIfToFields : function(o){
45179         this.items.each(function(f){
45180            Roo.applyIf(f, o);
45181         });
45182         return this;
45183     }
45184 });
45185
45186 // back compat
45187 Roo.BasicForm = Roo.form.BasicForm;/*
45188  * Based on:
45189  * Ext JS Library 1.1.1
45190  * Copyright(c) 2006-2007, Ext JS, LLC.
45191  *
45192  * Originally Released Under LGPL - original licence link has changed is not relivant.
45193  *
45194  * Fork - LGPL
45195  * <script type="text/javascript">
45196  */
45197
45198 /**
45199  * @class Roo.form.Form
45200  * @extends Roo.form.BasicForm
45201  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45202  * @constructor
45203  * @param {Object} config Configuration options
45204  */
45205 Roo.form.Form = function(config){
45206     var xitems =  [];
45207     if (config.items) {
45208         xitems = config.items;
45209         delete config.items;
45210     }
45211    
45212     
45213     Roo.form.Form.superclass.constructor.call(this, null, config);
45214     this.url = this.url || this.action;
45215     if(!this.root){
45216         this.root = new Roo.form.Layout(Roo.applyIf({
45217             id: Roo.id()
45218         }, config));
45219     }
45220     this.active = this.root;
45221     /**
45222      * Array of all the buttons that have been added to this form via {@link addButton}
45223      * @type Array
45224      */
45225     this.buttons = [];
45226     this.allItems = [];
45227     this.addEvents({
45228         /**
45229          * @event clientvalidation
45230          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45231          * @param {Form} this
45232          * @param {Boolean} valid true if the form has passed client-side validation
45233          */
45234         clientvalidation: true,
45235         /**
45236          * @event rendered
45237          * Fires when the form is rendered
45238          * @param {Roo.form.Form} form
45239          */
45240         rendered : true
45241     });
45242     
45243     if (this.progressUrl) {
45244             // push a hidden field onto the list of fields..
45245             this.addxtype( {
45246                     xns: Roo.form, 
45247                     xtype : 'Hidden', 
45248                     name : 'UPLOAD_IDENTIFIER' 
45249             });
45250         }
45251         
45252     
45253     Roo.each(xitems, this.addxtype, this);
45254     
45255     
45256     
45257 };
45258
45259 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45260     /**
45261      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45262      */
45263     /**
45264      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45265      */
45266     /**
45267      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45268      */
45269     buttonAlign:'center',
45270
45271     /**
45272      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45273      */
45274     minButtonWidth:75,
45275
45276     /**
45277      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45278      * This property cascades to child containers if not set.
45279      */
45280     labelAlign:'left',
45281
45282     /**
45283      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45284      * fires a looping event with that state. This is required to bind buttons to the valid
45285      * state using the config value formBind:true on the button.
45286      */
45287     monitorValid : false,
45288
45289     /**
45290      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45291      */
45292     monitorPoll : 200,
45293     
45294     /**
45295      * @cfg {String} progressUrl - Url to return progress data 
45296      */
45297     
45298     progressUrl : false,
45299   
45300     /**
45301      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45302      * fields are added and the column is closed. If no fields are passed the column remains open
45303      * until end() is called.
45304      * @param {Object} config The config to pass to the column
45305      * @param {Field} field1 (optional)
45306      * @param {Field} field2 (optional)
45307      * @param {Field} etc (optional)
45308      * @return Column The column container object
45309      */
45310     column : function(c){
45311         var col = new Roo.form.Column(c);
45312         this.start(col);
45313         if(arguments.length > 1){ // duplicate code required because of Opera
45314             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45315             this.end();
45316         }
45317         return col;
45318     },
45319
45320     /**
45321      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45322      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45323      * until end() is called.
45324      * @param {Object} config The config to pass to the fieldset
45325      * @param {Field} field1 (optional)
45326      * @param {Field} field2 (optional)
45327      * @param {Field} etc (optional)
45328      * @return FieldSet The fieldset container object
45329      */
45330     fieldset : function(c){
45331         var fs = new Roo.form.FieldSet(c);
45332         this.start(fs);
45333         if(arguments.length > 1){ // duplicate code required because of Opera
45334             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45335             this.end();
45336         }
45337         return fs;
45338     },
45339
45340     /**
45341      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45342      * fields are added and the container is closed. If no fields are passed the container remains open
45343      * until end() is called.
45344      * @param {Object} config The config to pass to the Layout
45345      * @param {Field} field1 (optional)
45346      * @param {Field} field2 (optional)
45347      * @param {Field} etc (optional)
45348      * @return Layout The container object
45349      */
45350     container : function(c){
45351         var l = new Roo.form.Layout(c);
45352         this.start(l);
45353         if(arguments.length > 1){ // duplicate code required because of Opera
45354             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45355             this.end();
45356         }
45357         return l;
45358     },
45359
45360     /**
45361      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45362      * @param {Object} container A Roo.form.Layout or subclass of Layout
45363      * @return {Form} this
45364      */
45365     start : function(c){
45366         // cascade label info
45367         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45368         this.active.stack.push(c);
45369         c.ownerCt = this.active;
45370         this.active = c;
45371         return this;
45372     },
45373
45374     /**
45375      * Closes the current open container
45376      * @return {Form} this
45377      */
45378     end : function(){
45379         if(this.active == this.root){
45380             return this;
45381         }
45382         this.active = this.active.ownerCt;
45383         return this;
45384     },
45385
45386     /**
45387      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45388      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45389      * as the label of the field.
45390      * @param {Field} field1
45391      * @param {Field} field2 (optional)
45392      * @param {Field} etc. (optional)
45393      * @return {Form} this
45394      */
45395     add : function(){
45396         this.active.stack.push.apply(this.active.stack, arguments);
45397         this.allItems.push.apply(this.allItems,arguments);
45398         var r = [];
45399         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45400             if(a[i].isFormField){
45401                 r.push(a[i]);
45402             }
45403         }
45404         if(r.length > 0){
45405             Roo.form.Form.superclass.add.apply(this, r);
45406         }
45407         return this;
45408     },
45409     
45410
45411     
45412     
45413     
45414      /**
45415      * Find any element that has been added to a form, using it's ID or name
45416      * This can include framesets, columns etc. along with regular fields..
45417      * @param {String} id - id or name to find.
45418      
45419      * @return {Element} e - or false if nothing found.
45420      */
45421     findbyId : function(id)
45422     {
45423         var ret = false;
45424         if (!id) {
45425             return ret;
45426         }
45427         Roo.each(this.allItems, function(f){
45428             if (f.id == id || f.name == id ){
45429                 ret = f;
45430                 return false;
45431             }
45432         });
45433         return ret;
45434     },
45435
45436     
45437     
45438     /**
45439      * Render this form into the passed container. This should only be called once!
45440      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45441      * @return {Form} this
45442      */
45443     render : function(ct)
45444     {
45445         
45446         
45447         
45448         ct = Roo.get(ct);
45449         var o = this.autoCreate || {
45450             tag: 'form',
45451             method : this.method || 'POST',
45452             id : this.id || Roo.id()
45453         };
45454         this.initEl(ct.createChild(o));
45455
45456         this.root.render(this.el);
45457         
45458        
45459              
45460         this.items.each(function(f){
45461             f.render('x-form-el-'+f.id);
45462         });
45463
45464         if(this.buttons.length > 0){
45465             // tables are required to maintain order and for correct IE layout
45466             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45467                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45468                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45469             }}, null, true);
45470             var tr = tb.getElementsByTagName('tr')[0];
45471             for(var i = 0, len = this.buttons.length; i < len; i++) {
45472                 var b = this.buttons[i];
45473                 var td = document.createElement('td');
45474                 td.className = 'x-form-btn-td';
45475                 b.render(tr.appendChild(td));
45476             }
45477         }
45478         if(this.monitorValid){ // initialize after render
45479             this.startMonitoring();
45480         }
45481         this.fireEvent('rendered', this);
45482         return this;
45483     },
45484
45485     /**
45486      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45487      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45488      * object or a valid Roo.DomHelper element config
45489      * @param {Function} handler The function called when the button is clicked
45490      * @param {Object} scope (optional) The scope of the handler function
45491      * @return {Roo.Button}
45492      */
45493     addButton : function(config, handler, scope){
45494         var bc = {
45495             handler: handler,
45496             scope: scope,
45497             minWidth: this.minButtonWidth,
45498             hideParent:true
45499         };
45500         if(typeof config == "string"){
45501             bc.text = config;
45502         }else{
45503             Roo.apply(bc, config);
45504         }
45505         var btn = new Roo.Button(null, bc);
45506         this.buttons.push(btn);
45507         return btn;
45508     },
45509
45510      /**
45511      * Adds a series of form elements (using the xtype property as the factory method.
45512      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45513      * @param {Object} config 
45514      */
45515     
45516     addxtype : function()
45517     {
45518         var ar = Array.prototype.slice.call(arguments, 0);
45519         var ret = false;
45520         for(var i = 0; i < ar.length; i++) {
45521             if (!ar[i]) {
45522                 continue; // skip -- if this happends something invalid got sent, we 
45523                 // should ignore it, as basically that interface element will not show up
45524                 // and that should be pretty obvious!!
45525             }
45526             
45527             if (Roo.form[ar[i].xtype]) {
45528                 ar[i].form = this;
45529                 var fe = Roo.factory(ar[i], Roo.form);
45530                 if (!ret) {
45531                     ret = fe;
45532                 }
45533                 fe.form = this;
45534                 if (fe.store) {
45535                     fe.store.form = this;
45536                 }
45537                 if (fe.isLayout) {  
45538                          
45539                     this.start(fe);
45540                     this.allItems.push(fe);
45541                     if (fe.items && fe.addxtype) {
45542                         fe.addxtype.apply(fe, fe.items);
45543                         delete fe.items;
45544                     }
45545                      this.end();
45546                     continue;
45547                 }
45548                 
45549                 
45550                  
45551                 this.add(fe);
45552               //  console.log('adding ' + ar[i].xtype);
45553             }
45554             if (ar[i].xtype == 'Button') {  
45555                 //console.log('adding button');
45556                 //console.log(ar[i]);
45557                 this.addButton(ar[i]);
45558                 this.allItems.push(fe);
45559                 continue;
45560             }
45561             
45562             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45563                 alert('end is not supported on xtype any more, use items');
45564             //    this.end();
45565             //    //console.log('adding end');
45566             }
45567             
45568         }
45569         return ret;
45570     },
45571     
45572     /**
45573      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45574      * option "monitorValid"
45575      */
45576     startMonitoring : function(){
45577         if(!this.bound){
45578             this.bound = true;
45579             Roo.TaskMgr.start({
45580                 run : this.bindHandler,
45581                 interval : this.monitorPoll || 200,
45582                 scope: this
45583             });
45584         }
45585     },
45586
45587     /**
45588      * Stops monitoring of the valid state of this form
45589      */
45590     stopMonitoring : function(){
45591         this.bound = false;
45592     },
45593
45594     // private
45595     bindHandler : function(){
45596         if(!this.bound){
45597             return false; // stops binding
45598         }
45599         var valid = true;
45600         this.items.each(function(f){
45601             if(!f.isValid(true)){
45602                 valid = false;
45603                 return false;
45604             }
45605         });
45606         for(var i = 0, len = this.buttons.length; i < len; i++){
45607             var btn = this.buttons[i];
45608             if(btn.formBind === true && btn.disabled === valid){
45609                 btn.setDisabled(!valid);
45610             }
45611         }
45612         this.fireEvent('clientvalidation', this, valid);
45613     }
45614     
45615     
45616     
45617     
45618     
45619     
45620     
45621     
45622 });
45623
45624
45625 // back compat
45626 Roo.Form = Roo.form.Form;
45627 /*
45628  * Based on:
45629  * Ext JS Library 1.1.1
45630  * Copyright(c) 2006-2007, Ext JS, LLC.
45631  *
45632  * Originally Released Under LGPL - original licence link has changed is not relivant.
45633  *
45634  * Fork - LGPL
45635  * <script type="text/javascript">
45636  */
45637
45638 // as we use this in bootstrap.
45639 Roo.namespace('Roo.form');
45640  /**
45641  * @class Roo.form.Action
45642  * Internal Class used to handle form actions
45643  * @constructor
45644  * @param {Roo.form.BasicForm} el The form element or its id
45645  * @param {Object} config Configuration options
45646  */
45647
45648  
45649  
45650 // define the action interface
45651 Roo.form.Action = function(form, options){
45652     this.form = form;
45653     this.options = options || {};
45654 };
45655 /**
45656  * Client Validation Failed
45657  * @const 
45658  */
45659 Roo.form.Action.CLIENT_INVALID = 'client';
45660 /**
45661  * Server Validation Failed
45662  * @const 
45663  */
45664 Roo.form.Action.SERVER_INVALID = 'server';
45665  /**
45666  * Connect to Server Failed
45667  * @const 
45668  */
45669 Roo.form.Action.CONNECT_FAILURE = 'connect';
45670 /**
45671  * Reading Data from Server Failed
45672  * @const 
45673  */
45674 Roo.form.Action.LOAD_FAILURE = 'load';
45675
45676 Roo.form.Action.prototype = {
45677     type : 'default',
45678     failureType : undefined,
45679     response : undefined,
45680     result : undefined,
45681
45682     // interface method
45683     run : function(options){
45684
45685     },
45686
45687     // interface method
45688     success : function(response){
45689
45690     },
45691
45692     // interface method
45693     handleResponse : function(response){
45694
45695     },
45696
45697     // default connection failure
45698     failure : function(response){
45699         
45700         this.response = response;
45701         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45702         this.form.afterAction(this, false);
45703     },
45704
45705     processResponse : function(response){
45706         this.response = response;
45707         if(!response.responseText){
45708             return true;
45709         }
45710         this.result = this.handleResponse(response);
45711         return this.result;
45712     },
45713
45714     // utility functions used internally
45715     getUrl : function(appendParams){
45716         var url = this.options.url || this.form.url || this.form.el.dom.action;
45717         if(appendParams){
45718             var p = this.getParams();
45719             if(p){
45720                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45721             }
45722         }
45723         return url;
45724     },
45725
45726     getMethod : function(){
45727         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45728     },
45729
45730     getParams : function(){
45731         var bp = this.form.baseParams;
45732         var p = this.options.params;
45733         if(p){
45734             if(typeof p == "object"){
45735                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45736             }else if(typeof p == 'string' && bp){
45737                 p += '&' + Roo.urlEncode(bp);
45738             }
45739         }else if(bp){
45740             p = Roo.urlEncode(bp);
45741         }
45742         return p;
45743     },
45744
45745     createCallback : function(){
45746         return {
45747             success: this.success,
45748             failure: this.failure,
45749             scope: this,
45750             timeout: (this.form.timeout*1000),
45751             upload: this.form.fileUpload ? this.success : undefined
45752         };
45753     }
45754 };
45755
45756 Roo.form.Action.Submit = function(form, options){
45757     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45758 };
45759
45760 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45761     type : 'submit',
45762
45763     haveProgress : false,
45764     uploadComplete : false,
45765     
45766     // uploadProgress indicator.
45767     uploadProgress : function()
45768     {
45769         if (!this.form.progressUrl) {
45770             return;
45771         }
45772         
45773         if (!this.haveProgress) {
45774             Roo.MessageBox.progress("Uploading", "Uploading");
45775         }
45776         if (this.uploadComplete) {
45777            Roo.MessageBox.hide();
45778            return;
45779         }
45780         
45781         this.haveProgress = true;
45782    
45783         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45784         
45785         var c = new Roo.data.Connection();
45786         c.request({
45787             url : this.form.progressUrl,
45788             params: {
45789                 id : uid
45790             },
45791             method: 'GET',
45792             success : function(req){
45793                //console.log(data);
45794                 var rdata = false;
45795                 var edata;
45796                 try  {
45797                    rdata = Roo.decode(req.responseText)
45798                 } catch (e) {
45799                     Roo.log("Invalid data from server..");
45800                     Roo.log(edata);
45801                     return;
45802                 }
45803                 if (!rdata || !rdata.success) {
45804                     Roo.log(rdata);
45805                     Roo.MessageBox.alert(Roo.encode(rdata));
45806                     return;
45807                 }
45808                 var data = rdata.data;
45809                 
45810                 if (this.uploadComplete) {
45811                    Roo.MessageBox.hide();
45812                    return;
45813                 }
45814                    
45815                 if (data){
45816                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45817                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45818                     );
45819                 }
45820                 this.uploadProgress.defer(2000,this);
45821             },
45822        
45823             failure: function(data) {
45824                 Roo.log('progress url failed ');
45825                 Roo.log(data);
45826             },
45827             scope : this
45828         });
45829            
45830     },
45831     
45832     
45833     run : function()
45834     {
45835         // run get Values on the form, so it syncs any secondary forms.
45836         this.form.getValues();
45837         
45838         var o = this.options;
45839         var method = this.getMethod();
45840         var isPost = method == 'POST';
45841         if(o.clientValidation === false || this.form.isValid()){
45842             
45843             if (this.form.progressUrl) {
45844                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45845                     (new Date() * 1) + '' + Math.random());
45846                     
45847             } 
45848             
45849             
45850             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45851                 form:this.form.el.dom,
45852                 url:this.getUrl(!isPost),
45853                 method: method,
45854                 params:isPost ? this.getParams() : null,
45855                 isUpload: this.form.fileUpload
45856             }));
45857             
45858             this.uploadProgress();
45859
45860         }else if (o.clientValidation !== false){ // client validation failed
45861             this.failureType = Roo.form.Action.CLIENT_INVALID;
45862             this.form.afterAction(this, false);
45863         }
45864     },
45865
45866     success : function(response)
45867     {
45868         this.uploadComplete= true;
45869         if (this.haveProgress) {
45870             Roo.MessageBox.hide();
45871         }
45872         
45873         
45874         var result = this.processResponse(response);
45875         if(result === true || result.success){
45876             this.form.afterAction(this, true);
45877             return;
45878         }
45879         if(result.errors){
45880             this.form.markInvalid(result.errors);
45881             this.failureType = Roo.form.Action.SERVER_INVALID;
45882         }
45883         this.form.afterAction(this, false);
45884     },
45885     failure : function(response)
45886     {
45887         this.uploadComplete= true;
45888         if (this.haveProgress) {
45889             Roo.MessageBox.hide();
45890         }
45891         
45892         this.response = response;
45893         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45894         this.form.afterAction(this, false);
45895     },
45896     
45897     handleResponse : function(response){
45898         if(this.form.errorReader){
45899             var rs = this.form.errorReader.read(response);
45900             var errors = [];
45901             if(rs.records){
45902                 for(var i = 0, len = rs.records.length; i < len; i++) {
45903                     var r = rs.records[i];
45904                     errors[i] = r.data;
45905                 }
45906             }
45907             if(errors.length < 1){
45908                 errors = null;
45909             }
45910             return {
45911                 success : rs.success,
45912                 errors : errors
45913             };
45914         }
45915         var ret = false;
45916         try {
45917             ret = Roo.decode(response.responseText);
45918         } catch (e) {
45919             ret = {
45920                 success: false,
45921                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45922                 errors : []
45923             };
45924         }
45925         return ret;
45926         
45927     }
45928 });
45929
45930
45931 Roo.form.Action.Load = function(form, options){
45932     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45933     this.reader = this.form.reader;
45934 };
45935
45936 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45937     type : 'load',
45938
45939     run : function(){
45940         
45941         Roo.Ajax.request(Roo.apply(
45942                 this.createCallback(), {
45943                     method:this.getMethod(),
45944                     url:this.getUrl(false),
45945                     params:this.getParams()
45946         }));
45947     },
45948
45949     success : function(response){
45950         
45951         var result = this.processResponse(response);
45952         if(result === true || !result.success || !result.data){
45953             this.failureType = Roo.form.Action.LOAD_FAILURE;
45954             this.form.afterAction(this, false);
45955             return;
45956         }
45957         this.form.clearInvalid();
45958         this.form.setValues(result.data);
45959         this.form.afterAction(this, true);
45960     },
45961
45962     handleResponse : function(response){
45963         if(this.form.reader){
45964             var rs = this.form.reader.read(response);
45965             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45966             return {
45967                 success : rs.success,
45968                 data : data
45969             };
45970         }
45971         return Roo.decode(response.responseText);
45972     }
45973 });
45974
45975 Roo.form.Action.ACTION_TYPES = {
45976     'load' : Roo.form.Action.Load,
45977     'submit' : Roo.form.Action.Submit
45978 };/*
45979  * Based on:
45980  * Ext JS Library 1.1.1
45981  * Copyright(c) 2006-2007, Ext JS, LLC.
45982  *
45983  * Originally Released Under LGPL - original licence link has changed is not relivant.
45984  *
45985  * Fork - LGPL
45986  * <script type="text/javascript">
45987  */
45988  
45989 /**
45990  * @class Roo.form.Layout
45991  * @extends Roo.Component
45992  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45993  * @constructor
45994  * @param {Object} config Configuration options
45995  */
45996 Roo.form.Layout = function(config){
45997     var xitems = [];
45998     if (config.items) {
45999         xitems = config.items;
46000         delete config.items;
46001     }
46002     Roo.form.Layout.superclass.constructor.call(this, config);
46003     this.stack = [];
46004     Roo.each(xitems, this.addxtype, this);
46005      
46006 };
46007
46008 Roo.extend(Roo.form.Layout, Roo.Component, {
46009     /**
46010      * @cfg {String/Object} autoCreate
46011      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46012      */
46013     /**
46014      * @cfg {String/Object/Function} style
46015      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46016      * a function which returns such a specification.
46017      */
46018     /**
46019      * @cfg {String} labelAlign
46020      * Valid values are "left," "top" and "right" (defaults to "left")
46021      */
46022     /**
46023      * @cfg {Number} labelWidth
46024      * Fixed width in pixels of all field labels (defaults to undefined)
46025      */
46026     /**
46027      * @cfg {Boolean} clear
46028      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46029      */
46030     clear : true,
46031     /**
46032      * @cfg {String} labelSeparator
46033      * The separator to use after field labels (defaults to ':')
46034      */
46035     labelSeparator : ':',
46036     /**
46037      * @cfg {Boolean} hideLabels
46038      * True to suppress the display of field labels in this layout (defaults to false)
46039      */
46040     hideLabels : false,
46041
46042     // private
46043     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46044     
46045     isLayout : true,
46046     
46047     // private
46048     onRender : function(ct, position){
46049         if(this.el){ // from markup
46050             this.el = Roo.get(this.el);
46051         }else {  // generate
46052             var cfg = this.getAutoCreate();
46053             this.el = ct.createChild(cfg, position);
46054         }
46055         if(this.style){
46056             this.el.applyStyles(this.style);
46057         }
46058         if(this.labelAlign){
46059             this.el.addClass('x-form-label-'+this.labelAlign);
46060         }
46061         if(this.hideLabels){
46062             this.labelStyle = "display:none";
46063             this.elementStyle = "padding-left:0;";
46064         }else{
46065             if(typeof this.labelWidth == 'number'){
46066                 this.labelStyle = "width:"+this.labelWidth+"px;";
46067                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46068             }
46069             if(this.labelAlign == 'top'){
46070                 this.labelStyle = "width:auto;";
46071                 this.elementStyle = "padding-left:0;";
46072             }
46073         }
46074         var stack = this.stack;
46075         var slen = stack.length;
46076         if(slen > 0){
46077             if(!this.fieldTpl){
46078                 var t = new Roo.Template(
46079                     '<div class="x-form-item {5}">',
46080                         '<label for="{0}" style="{2}">{1}{4}</label>',
46081                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46082                         '</div>',
46083                     '</div><div class="x-form-clear-left"></div>'
46084                 );
46085                 t.disableFormats = true;
46086                 t.compile();
46087                 Roo.form.Layout.prototype.fieldTpl = t;
46088             }
46089             for(var i = 0; i < slen; i++) {
46090                 if(stack[i].isFormField){
46091                     this.renderField(stack[i]);
46092                 }else{
46093                     this.renderComponent(stack[i]);
46094                 }
46095             }
46096         }
46097         if(this.clear){
46098             this.el.createChild({cls:'x-form-clear'});
46099         }
46100     },
46101
46102     // private
46103     renderField : function(f){
46104         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46105                f.id, //0
46106                f.fieldLabel, //1
46107                f.labelStyle||this.labelStyle||'', //2
46108                this.elementStyle||'', //3
46109                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46110                f.itemCls||this.itemCls||''  //5
46111        ], true).getPrevSibling());
46112     },
46113
46114     // private
46115     renderComponent : function(c){
46116         c.render(c.isLayout ? this.el : this.el.createChild());    
46117     },
46118     /**
46119      * Adds a object form elements (using the xtype property as the factory method.)
46120      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46121      * @param {Object} config 
46122      */
46123     addxtype : function(o)
46124     {
46125         // create the lement.
46126         o.form = this.form;
46127         var fe = Roo.factory(o, Roo.form);
46128         this.form.allItems.push(fe);
46129         this.stack.push(fe);
46130         
46131         if (fe.isFormField) {
46132             this.form.items.add(fe);
46133         }
46134          
46135         return fe;
46136     }
46137 });
46138
46139 /**
46140  * @class Roo.form.Column
46141  * @extends Roo.form.Layout
46142  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46143  * @constructor
46144  * @param {Object} config Configuration options
46145  */
46146 Roo.form.Column = function(config){
46147     Roo.form.Column.superclass.constructor.call(this, config);
46148 };
46149
46150 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46151     /**
46152      * @cfg {Number/String} width
46153      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46154      */
46155     /**
46156      * @cfg {String/Object} autoCreate
46157      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46158      */
46159
46160     // private
46161     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46162
46163     // private
46164     onRender : function(ct, position){
46165         Roo.form.Column.superclass.onRender.call(this, ct, position);
46166         if(this.width){
46167             this.el.setWidth(this.width);
46168         }
46169     }
46170 });
46171
46172
46173 /**
46174  * @class Roo.form.Row
46175  * @extends Roo.form.Layout
46176  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46177  * @constructor
46178  * @param {Object} config Configuration options
46179  */
46180
46181  
46182 Roo.form.Row = function(config){
46183     Roo.form.Row.superclass.constructor.call(this, config);
46184 };
46185  
46186 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46187       /**
46188      * @cfg {Number/String} width
46189      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46190      */
46191     /**
46192      * @cfg {Number/String} height
46193      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46194      */
46195     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46196     
46197     padWidth : 20,
46198     // private
46199     onRender : function(ct, position){
46200         //console.log('row render');
46201         if(!this.rowTpl){
46202             var t = new Roo.Template(
46203                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46204                     '<label for="{0}" style="{2}">{1}{4}</label>',
46205                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46206                     '</div>',
46207                 '</div>'
46208             );
46209             t.disableFormats = true;
46210             t.compile();
46211             Roo.form.Layout.prototype.rowTpl = t;
46212         }
46213         this.fieldTpl = this.rowTpl;
46214         
46215         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46216         var labelWidth = 100;
46217         
46218         if ((this.labelAlign != 'top')) {
46219             if (typeof this.labelWidth == 'number') {
46220                 labelWidth = this.labelWidth
46221             }
46222             this.padWidth =  20 + labelWidth;
46223             
46224         }
46225         
46226         Roo.form.Column.superclass.onRender.call(this, ct, position);
46227         if(this.width){
46228             this.el.setWidth(this.width);
46229         }
46230         if(this.height){
46231             this.el.setHeight(this.height);
46232         }
46233     },
46234     
46235     // private
46236     renderField : function(f){
46237         f.fieldEl = this.fieldTpl.append(this.el, [
46238                f.id, f.fieldLabel,
46239                f.labelStyle||this.labelStyle||'',
46240                this.elementStyle||'',
46241                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46242                f.itemCls||this.itemCls||'',
46243                f.width ? f.width + this.padWidth : 160 + this.padWidth
46244        ],true);
46245     }
46246 });
46247  
46248
46249 /**
46250  * @class Roo.form.FieldSet
46251  * @extends Roo.form.Layout
46252  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46253  * @constructor
46254  * @param {Object} config Configuration options
46255  */
46256 Roo.form.FieldSet = function(config){
46257     Roo.form.FieldSet.superclass.constructor.call(this, config);
46258 };
46259
46260 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46261     /**
46262      * @cfg {String} legend
46263      * The text to display as the legend for the FieldSet (defaults to '')
46264      */
46265     /**
46266      * @cfg {String/Object} autoCreate
46267      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46268      */
46269
46270     // private
46271     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46272
46273     // private
46274     onRender : function(ct, position){
46275         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46276         if(this.legend){
46277             this.setLegend(this.legend);
46278         }
46279     },
46280
46281     // private
46282     setLegend : function(text){
46283         if(this.rendered){
46284             this.el.child('legend').update(text);
46285         }
46286     }
46287 });/*
46288  * Based on:
46289  * Ext JS Library 1.1.1
46290  * Copyright(c) 2006-2007, Ext JS, LLC.
46291  *
46292  * Originally Released Under LGPL - original licence link has changed is not relivant.
46293  *
46294  * Fork - LGPL
46295  * <script type="text/javascript">
46296  */
46297 /**
46298  * @class Roo.form.VTypes
46299  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46300  * @singleton
46301  */
46302 Roo.form.VTypes = function(){
46303     // closure these in so they are only created once.
46304     var alpha = /^[a-zA-Z_]+$/;
46305     var alphanum = /^[a-zA-Z0-9_]+$/;
46306     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46307     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46308
46309     // All these messages and functions are configurable
46310     return {
46311         /**
46312          * The function used to validate email addresses
46313          * @param {String} value The email address
46314          */
46315         'email' : function(v){
46316             return email.test(v);
46317         },
46318         /**
46319          * The error text to display when the email validation function returns false
46320          * @type String
46321          */
46322         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46323         /**
46324          * The keystroke filter mask to be applied on email input
46325          * @type RegExp
46326          */
46327         'emailMask' : /[a-z0-9_\.\-@]/i,
46328
46329         /**
46330          * The function used to validate URLs
46331          * @param {String} value The URL
46332          */
46333         'url' : function(v){
46334             return url.test(v);
46335         },
46336         /**
46337          * The error text to display when the url validation function returns false
46338          * @type String
46339          */
46340         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46341         
46342         /**
46343          * The function used to validate alpha values
46344          * @param {String} value The value
46345          */
46346         'alpha' : function(v){
46347             return alpha.test(v);
46348         },
46349         /**
46350          * The error text to display when the alpha validation function returns false
46351          * @type String
46352          */
46353         'alphaText' : 'This field should only contain letters and _',
46354         /**
46355          * The keystroke filter mask to be applied on alpha input
46356          * @type RegExp
46357          */
46358         'alphaMask' : /[a-z_]/i,
46359
46360         /**
46361          * The function used to validate alphanumeric values
46362          * @param {String} value The value
46363          */
46364         'alphanum' : function(v){
46365             return alphanum.test(v);
46366         },
46367         /**
46368          * The error text to display when the alphanumeric validation function returns false
46369          * @type String
46370          */
46371         'alphanumText' : 'This field should only contain letters, numbers and _',
46372         /**
46373          * The keystroke filter mask to be applied on alphanumeric input
46374          * @type RegExp
46375          */
46376         'alphanumMask' : /[a-z0-9_]/i
46377     };
46378 }();//<script type="text/javascript">
46379
46380 /**
46381  * @class Roo.form.FCKeditor
46382  * @extends Roo.form.TextArea
46383  * Wrapper around the FCKEditor http://www.fckeditor.net
46384  * @constructor
46385  * Creates a new FCKeditor
46386  * @param {Object} config Configuration options
46387  */
46388 Roo.form.FCKeditor = function(config){
46389     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46390     this.addEvents({
46391          /**
46392          * @event editorinit
46393          * Fired when the editor is initialized - you can add extra handlers here..
46394          * @param {FCKeditor} this
46395          * @param {Object} the FCK object.
46396          */
46397         editorinit : true
46398     });
46399     
46400     
46401 };
46402 Roo.form.FCKeditor.editors = { };
46403 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46404 {
46405     //defaultAutoCreate : {
46406     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46407     //},
46408     // private
46409     /**
46410      * @cfg {Object} fck options - see fck manual for details.
46411      */
46412     fckconfig : false,
46413     
46414     /**
46415      * @cfg {Object} fck toolbar set (Basic or Default)
46416      */
46417     toolbarSet : 'Basic',
46418     /**
46419      * @cfg {Object} fck BasePath
46420      */ 
46421     basePath : '/fckeditor/',
46422     
46423     
46424     frame : false,
46425     
46426     value : '',
46427     
46428    
46429     onRender : function(ct, position)
46430     {
46431         if(!this.el){
46432             this.defaultAutoCreate = {
46433                 tag: "textarea",
46434                 style:"width:300px;height:60px;",
46435                 autocomplete: "off"
46436             };
46437         }
46438         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46439         /*
46440         if(this.grow){
46441             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46442             if(this.preventScrollbars){
46443                 this.el.setStyle("overflow", "hidden");
46444             }
46445             this.el.setHeight(this.growMin);
46446         }
46447         */
46448         //console.log('onrender' + this.getId() );
46449         Roo.form.FCKeditor.editors[this.getId()] = this;
46450          
46451
46452         this.replaceTextarea() ;
46453         
46454     },
46455     
46456     getEditor : function() {
46457         return this.fckEditor;
46458     },
46459     /**
46460      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46461      * @param {Mixed} value The value to set
46462      */
46463     
46464     
46465     setValue : function(value)
46466     {
46467         //console.log('setValue: ' + value);
46468         
46469         if(typeof(value) == 'undefined') { // not sure why this is happending...
46470             return;
46471         }
46472         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46473         
46474         //if(!this.el || !this.getEditor()) {
46475         //    this.value = value;
46476             //this.setValue.defer(100,this,[value]);    
46477         //    return;
46478         //} 
46479         
46480         if(!this.getEditor()) {
46481             return;
46482         }
46483         
46484         this.getEditor().SetData(value);
46485         
46486         //
46487
46488     },
46489
46490     /**
46491      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46492      * @return {Mixed} value The field value
46493      */
46494     getValue : function()
46495     {
46496         
46497         if (this.frame && this.frame.dom.style.display == 'none') {
46498             return Roo.form.FCKeditor.superclass.getValue.call(this);
46499         }
46500         
46501         if(!this.el || !this.getEditor()) {
46502            
46503            // this.getValue.defer(100,this); 
46504             return this.value;
46505         }
46506        
46507         
46508         var value=this.getEditor().GetData();
46509         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46510         return Roo.form.FCKeditor.superclass.getValue.call(this);
46511         
46512
46513     },
46514
46515     /**
46516      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46517      * @return {Mixed} value The field value
46518      */
46519     getRawValue : function()
46520     {
46521         if (this.frame && this.frame.dom.style.display == 'none') {
46522             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46523         }
46524         
46525         if(!this.el || !this.getEditor()) {
46526             //this.getRawValue.defer(100,this); 
46527             return this.value;
46528             return;
46529         }
46530         
46531         
46532         
46533         var value=this.getEditor().GetData();
46534         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46535         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46536          
46537     },
46538     
46539     setSize : function(w,h) {
46540         
46541         
46542         
46543         //if (this.frame && this.frame.dom.style.display == 'none') {
46544         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46545         //    return;
46546         //}
46547         //if(!this.el || !this.getEditor()) {
46548         //    this.setSize.defer(100,this, [w,h]); 
46549         //    return;
46550         //}
46551         
46552         
46553         
46554         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46555         
46556         this.frame.dom.setAttribute('width', w);
46557         this.frame.dom.setAttribute('height', h);
46558         this.frame.setSize(w,h);
46559         
46560     },
46561     
46562     toggleSourceEdit : function(value) {
46563         
46564       
46565          
46566         this.el.dom.style.display = value ? '' : 'none';
46567         this.frame.dom.style.display = value ?  'none' : '';
46568         
46569     },
46570     
46571     
46572     focus: function(tag)
46573     {
46574         if (this.frame.dom.style.display == 'none') {
46575             return Roo.form.FCKeditor.superclass.focus.call(this);
46576         }
46577         if(!this.el || !this.getEditor()) {
46578             this.focus.defer(100,this, [tag]); 
46579             return;
46580         }
46581         
46582         
46583         
46584         
46585         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46586         this.getEditor().Focus();
46587         if (tgs.length) {
46588             if (!this.getEditor().Selection.GetSelection()) {
46589                 this.focus.defer(100,this, [tag]); 
46590                 return;
46591             }
46592             
46593             
46594             var r = this.getEditor().EditorDocument.createRange();
46595             r.setStart(tgs[0],0);
46596             r.setEnd(tgs[0],0);
46597             this.getEditor().Selection.GetSelection().removeAllRanges();
46598             this.getEditor().Selection.GetSelection().addRange(r);
46599             this.getEditor().Focus();
46600         }
46601         
46602     },
46603     
46604     
46605     
46606     replaceTextarea : function()
46607     {
46608         if ( document.getElementById( this.getId() + '___Frame' ) )
46609             return ;
46610         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46611         //{
46612             // We must check the elements firstly using the Id and then the name.
46613         var oTextarea = document.getElementById( this.getId() );
46614         
46615         var colElementsByName = document.getElementsByName( this.getId() ) ;
46616          
46617         oTextarea.style.display = 'none' ;
46618
46619         if ( oTextarea.tabIndex ) {            
46620             this.TabIndex = oTextarea.tabIndex ;
46621         }
46622         
46623         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46624         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46625         this.frame = Roo.get(this.getId() + '___Frame')
46626     },
46627     
46628     _getConfigHtml : function()
46629     {
46630         var sConfig = '' ;
46631
46632         for ( var o in this.fckconfig ) {
46633             sConfig += sConfig.length > 0  ? '&amp;' : '';
46634             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46635         }
46636
46637         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46638     },
46639     
46640     
46641     _getIFrameHtml : function()
46642     {
46643         var sFile = 'fckeditor.html' ;
46644         /* no idea what this is about..
46645         try
46646         {
46647             if ( (/fcksource=true/i).test( window.top.location.search ) )
46648                 sFile = 'fckeditor.original.html' ;
46649         }
46650         catch (e) { 
46651         */
46652
46653         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46654         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46655         
46656         
46657         var html = '<iframe id="' + this.getId() +
46658             '___Frame" src="' + sLink +
46659             '" width="' + this.width +
46660             '" height="' + this.height + '"' +
46661             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46662             ' frameborder="0" scrolling="no"></iframe>' ;
46663
46664         return html ;
46665     },
46666     
46667     _insertHtmlBefore : function( html, element )
46668     {
46669         if ( element.insertAdjacentHTML )       {
46670             // IE
46671             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46672         } else { // Gecko
46673             var oRange = document.createRange() ;
46674             oRange.setStartBefore( element ) ;
46675             var oFragment = oRange.createContextualFragment( html );
46676             element.parentNode.insertBefore( oFragment, element ) ;
46677         }
46678     }
46679     
46680     
46681   
46682     
46683     
46684     
46685     
46686
46687 });
46688
46689 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46690
46691 function FCKeditor_OnComplete(editorInstance){
46692     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46693     f.fckEditor = editorInstance;
46694     //console.log("loaded");
46695     f.fireEvent('editorinit', f, editorInstance);
46696
46697   
46698
46699  
46700
46701
46702
46703
46704
46705
46706
46707
46708
46709
46710
46711
46712
46713
46714
46715 //<script type="text/javascript">
46716 /**
46717  * @class Roo.form.GridField
46718  * @extends Roo.form.Field
46719  * Embed a grid (or editable grid into a form)
46720  * STATUS ALPHA
46721  * 
46722  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46723  * it needs 
46724  * xgrid.store = Roo.data.Store
46725  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46726  * xgrid.store.reader = Roo.data.JsonReader 
46727  * 
46728  * 
46729  * @constructor
46730  * Creates a new GridField
46731  * @param {Object} config Configuration options
46732  */
46733 Roo.form.GridField = function(config){
46734     Roo.form.GridField.superclass.constructor.call(this, config);
46735      
46736 };
46737
46738 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46739     /**
46740      * @cfg {Number} width  - used to restrict width of grid..
46741      */
46742     width : 100,
46743     /**
46744      * @cfg {Number} height - used to restrict height of grid..
46745      */
46746     height : 50,
46747      /**
46748      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46749          * 
46750          *}
46751      */
46752     xgrid : false, 
46753     /**
46754      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46755      * {tag: "input", type: "checkbox", autocomplete: "off"})
46756      */
46757    // defaultAutoCreate : { tag: 'div' },
46758     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46759     /**
46760      * @cfg {String} addTitle Text to include for adding a title.
46761      */
46762     addTitle : false,
46763     //
46764     onResize : function(){
46765         Roo.form.Field.superclass.onResize.apply(this, arguments);
46766     },
46767
46768     initEvents : function(){
46769         // Roo.form.Checkbox.superclass.initEvents.call(this);
46770         // has no events...
46771        
46772     },
46773
46774
46775     getResizeEl : function(){
46776         return this.wrap;
46777     },
46778
46779     getPositionEl : function(){
46780         return this.wrap;
46781     },
46782
46783     // private
46784     onRender : function(ct, position){
46785         
46786         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46787         var style = this.style;
46788         delete this.style;
46789         
46790         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46791         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46792         this.viewEl = this.wrap.createChild({ tag: 'div' });
46793         if (style) {
46794             this.viewEl.applyStyles(style);
46795         }
46796         if (this.width) {
46797             this.viewEl.setWidth(this.width);
46798         }
46799         if (this.height) {
46800             this.viewEl.setHeight(this.height);
46801         }
46802         //if(this.inputValue !== undefined){
46803         //this.setValue(this.value);
46804         
46805         
46806         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46807         
46808         
46809         this.grid.render();
46810         this.grid.getDataSource().on('remove', this.refreshValue, this);
46811         this.grid.getDataSource().on('update', this.refreshValue, this);
46812         this.grid.on('afteredit', this.refreshValue, this);
46813  
46814     },
46815      
46816     
46817     /**
46818      * Sets the value of the item. 
46819      * @param {String} either an object  or a string..
46820      */
46821     setValue : function(v){
46822         //this.value = v;
46823         v = v || []; // empty set..
46824         // this does not seem smart - it really only affects memoryproxy grids..
46825         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46826             var ds = this.grid.getDataSource();
46827             // assumes a json reader..
46828             var data = {}
46829             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46830             ds.loadData( data);
46831         }
46832         // clear selection so it does not get stale.
46833         if (this.grid.sm) { 
46834             this.grid.sm.clearSelections();
46835         }
46836         
46837         Roo.form.GridField.superclass.setValue.call(this, v);
46838         this.refreshValue();
46839         // should load data in the grid really....
46840     },
46841     
46842     // private
46843     refreshValue: function() {
46844          var val = [];
46845         this.grid.getDataSource().each(function(r) {
46846             val.push(r.data);
46847         });
46848         this.el.dom.value = Roo.encode(val);
46849     }
46850     
46851      
46852     
46853     
46854 });/*
46855  * Based on:
46856  * Ext JS Library 1.1.1
46857  * Copyright(c) 2006-2007, Ext JS, LLC.
46858  *
46859  * Originally Released Under LGPL - original licence link has changed is not relivant.
46860  *
46861  * Fork - LGPL
46862  * <script type="text/javascript">
46863  */
46864 /**
46865  * @class Roo.form.DisplayField
46866  * @extends Roo.form.Field
46867  * A generic Field to display non-editable data.
46868  * @constructor
46869  * Creates a new Display Field item.
46870  * @param {Object} config Configuration options
46871  */
46872 Roo.form.DisplayField = function(config){
46873     Roo.form.DisplayField.superclass.constructor.call(this, config);
46874     
46875 };
46876
46877 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46878     inputType:      'hidden',
46879     allowBlank:     true,
46880     readOnly:         true,
46881     
46882  
46883     /**
46884      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46885      */
46886     focusClass : undefined,
46887     /**
46888      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46889      */
46890     fieldClass: 'x-form-field',
46891     
46892      /**
46893      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46894      */
46895     valueRenderer: undefined,
46896     
46897     width: 100,
46898     /**
46899      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46900      * {tag: "input", type: "checkbox", autocomplete: "off"})
46901      */
46902      
46903  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46904
46905     onResize : function(){
46906         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46907         
46908     },
46909
46910     initEvents : function(){
46911         // Roo.form.Checkbox.superclass.initEvents.call(this);
46912         // has no events...
46913        
46914     },
46915
46916
46917     getResizeEl : function(){
46918         return this.wrap;
46919     },
46920
46921     getPositionEl : function(){
46922         return this.wrap;
46923     },
46924
46925     // private
46926     onRender : function(ct, position){
46927         
46928         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46929         //if(this.inputValue !== undefined){
46930         this.wrap = this.el.wrap();
46931         
46932         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46933         
46934         if (this.bodyStyle) {
46935             this.viewEl.applyStyles(this.bodyStyle);
46936         }
46937         //this.viewEl.setStyle('padding', '2px');
46938         
46939         this.setValue(this.value);
46940         
46941     },
46942 /*
46943     // private
46944     initValue : Roo.emptyFn,
46945
46946   */
46947
46948         // private
46949     onClick : function(){
46950         
46951     },
46952
46953     /**
46954      * Sets the checked state of the checkbox.
46955      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46956      */
46957     setValue : function(v){
46958         this.value = v;
46959         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46960         // this might be called before we have a dom element..
46961         if (!this.viewEl) {
46962             return;
46963         }
46964         this.viewEl.dom.innerHTML = html;
46965         Roo.form.DisplayField.superclass.setValue.call(this, v);
46966
46967     }
46968 });/*
46969  * 
46970  * Licence- LGPL
46971  * 
46972  */
46973
46974 /**
46975  * @class Roo.form.DayPicker
46976  * @extends Roo.form.Field
46977  * A Day picker show [M] [T] [W] ....
46978  * @constructor
46979  * Creates a new Day Picker
46980  * @param {Object} config Configuration options
46981  */
46982 Roo.form.DayPicker= function(config){
46983     Roo.form.DayPicker.superclass.constructor.call(this, config);
46984      
46985 };
46986
46987 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46988     /**
46989      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46990      */
46991     focusClass : undefined,
46992     /**
46993      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46994      */
46995     fieldClass: "x-form-field",
46996    
46997     /**
46998      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46999      * {tag: "input", type: "checkbox", autocomplete: "off"})
47000      */
47001     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
47002     
47003    
47004     actionMode : 'viewEl', 
47005     //
47006     // private
47007  
47008     inputType : 'hidden',
47009     
47010      
47011     inputElement: false, // real input element?
47012     basedOn: false, // ????
47013     
47014     isFormField: true, // not sure where this is needed!!!!
47015
47016     onResize : function(){
47017         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47018         if(!this.boxLabel){
47019             this.el.alignTo(this.wrap, 'c-c');
47020         }
47021     },
47022
47023     initEvents : function(){
47024         Roo.form.Checkbox.superclass.initEvents.call(this);
47025         this.el.on("click", this.onClick,  this);
47026         this.el.on("change", this.onClick,  this);
47027     },
47028
47029
47030     getResizeEl : function(){
47031         return this.wrap;
47032     },
47033
47034     getPositionEl : function(){
47035         return this.wrap;
47036     },
47037
47038     
47039     // private
47040     onRender : function(ct, position){
47041         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47042        
47043         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47044         
47045         var r1 = '<table><tr>';
47046         var r2 = '<tr class="x-form-daypick-icons">';
47047         for (var i=0; i < 7; i++) {
47048             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47049             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47050         }
47051         
47052         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47053         viewEl.select('img').on('click', this.onClick, this);
47054         this.viewEl = viewEl;   
47055         
47056         
47057         // this will not work on Chrome!!!
47058         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47059         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47060         
47061         
47062           
47063
47064     },
47065
47066     // private
47067     initValue : Roo.emptyFn,
47068
47069     /**
47070      * Returns the checked state of the checkbox.
47071      * @return {Boolean} True if checked, else false
47072      */
47073     getValue : function(){
47074         return this.el.dom.value;
47075         
47076     },
47077
47078         // private
47079     onClick : function(e){ 
47080         //this.setChecked(!this.checked);
47081         Roo.get(e.target).toggleClass('x-menu-item-checked');
47082         this.refreshValue();
47083         //if(this.el.dom.checked != this.checked){
47084         //    this.setValue(this.el.dom.checked);
47085        // }
47086     },
47087     
47088     // private
47089     refreshValue : function()
47090     {
47091         var val = '';
47092         this.viewEl.select('img',true).each(function(e,i,n)  {
47093             val += e.is(".x-menu-item-checked") ? String(n) : '';
47094         });
47095         this.setValue(val, true);
47096     },
47097
47098     /**
47099      * Sets the checked state of the checkbox.
47100      * On is always based on a string comparison between inputValue and the param.
47101      * @param {Boolean/String} value - the value to set 
47102      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47103      */
47104     setValue : function(v,suppressEvent){
47105         if (!this.el.dom) {
47106             return;
47107         }
47108         var old = this.el.dom.value ;
47109         this.el.dom.value = v;
47110         if (suppressEvent) {
47111             return ;
47112         }
47113          
47114         // update display..
47115         this.viewEl.select('img',true).each(function(e,i,n)  {
47116             
47117             var on = e.is(".x-menu-item-checked");
47118             var newv = v.indexOf(String(n)) > -1;
47119             if (on != newv) {
47120                 e.toggleClass('x-menu-item-checked');
47121             }
47122             
47123         });
47124         
47125         
47126         this.fireEvent('change', this, v, old);
47127         
47128         
47129     },
47130    
47131     // handle setting of hidden value by some other method!!?!?
47132     setFromHidden: function()
47133     {
47134         if(!this.el){
47135             return;
47136         }
47137         //console.log("SET FROM HIDDEN");
47138         //alert('setFrom hidden');
47139         this.setValue(this.el.dom.value);
47140     },
47141     
47142     onDestroy : function()
47143     {
47144         if(this.viewEl){
47145             Roo.get(this.viewEl).remove();
47146         }
47147          
47148         Roo.form.DayPicker.superclass.onDestroy.call(this);
47149     }
47150
47151 });/*
47152  * RooJS Library 1.1.1
47153  * Copyright(c) 2008-2011  Alan Knowles
47154  *
47155  * License - LGPL
47156  */
47157  
47158
47159 /**
47160  * @class Roo.form.ComboCheck
47161  * @extends Roo.form.ComboBox
47162  * A combobox for multiple select items.
47163  *
47164  * FIXME - could do with a reset button..
47165  * 
47166  * @constructor
47167  * Create a new ComboCheck
47168  * @param {Object} config Configuration options
47169  */
47170 Roo.form.ComboCheck = function(config){
47171     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47172     // should verify some data...
47173     // like
47174     // hiddenName = required..
47175     // displayField = required
47176     // valudField == required
47177     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47178     var _t = this;
47179     Roo.each(req, function(e) {
47180         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47181             throw "Roo.form.ComboCheck : missing value for: " + e;
47182         }
47183     });
47184     
47185     
47186 };
47187
47188 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47189      
47190      
47191     editable : false,
47192      
47193     selectedClass: 'x-menu-item-checked', 
47194     
47195     // private
47196     onRender : function(ct, position){
47197         var _t = this;
47198         
47199         
47200         
47201         if(!this.tpl){
47202             var cls = 'x-combo-list';
47203
47204             
47205             this.tpl =  new Roo.Template({
47206                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47207                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47208                    '<span>{' + this.displayField + '}</span>' +
47209                     '</div>' 
47210                 
47211             });
47212         }
47213  
47214         
47215         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47216         this.view.singleSelect = false;
47217         this.view.multiSelect = true;
47218         this.view.toggleSelect = true;
47219         this.pageTb.add(new Roo.Toolbar.Fill(), {
47220             
47221             text: 'Done',
47222             handler: function()
47223             {
47224                 _t.collapse();
47225             }
47226         });
47227     },
47228     
47229     onViewOver : function(e, t){
47230         // do nothing...
47231         return;
47232         
47233     },
47234     
47235     onViewClick : function(doFocus,index){
47236         return;
47237         
47238     },
47239     select: function () {
47240         //Roo.log("SELECT CALLED");
47241     },
47242      
47243     selectByValue : function(xv, scrollIntoView){
47244         var ar = this.getValueArray();
47245         var sels = [];
47246         
47247         Roo.each(ar, function(v) {
47248             if(v === undefined || v === null){
47249                 return;
47250             }
47251             var r = this.findRecord(this.valueField, v);
47252             if(r){
47253                 sels.push(this.store.indexOf(r))
47254                 
47255             }
47256         },this);
47257         this.view.select(sels);
47258         return false;
47259     },
47260     
47261     
47262     
47263     onSelect : function(record, index){
47264        // Roo.log("onselect Called");
47265        // this is only called by the clear button now..
47266         this.view.clearSelections();
47267         this.setValue('[]');
47268         if (this.value != this.valueBefore) {
47269             this.fireEvent('change', this, this.value, this.valueBefore);
47270             this.valueBefore = this.value;
47271         }
47272     },
47273     getValueArray : function()
47274     {
47275         var ar = [] ;
47276         
47277         try {
47278             //Roo.log(this.value);
47279             if (typeof(this.value) == 'undefined') {
47280                 return [];
47281             }
47282             var ar = Roo.decode(this.value);
47283             return  ar instanceof Array ? ar : []; //?? valid?
47284             
47285         } catch(e) {
47286             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47287             return [];
47288         }
47289          
47290     },
47291     expand : function ()
47292     {
47293         
47294         Roo.form.ComboCheck.superclass.expand.call(this);
47295         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47296         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47297         
47298
47299     },
47300     
47301     collapse : function(){
47302         Roo.form.ComboCheck.superclass.collapse.call(this);
47303         var sl = this.view.getSelectedIndexes();
47304         var st = this.store;
47305         var nv = [];
47306         var tv = [];
47307         var r;
47308         Roo.each(sl, function(i) {
47309             r = st.getAt(i);
47310             nv.push(r.get(this.valueField));
47311         },this);
47312         this.setValue(Roo.encode(nv));
47313         if (this.value != this.valueBefore) {
47314
47315             this.fireEvent('change', this, this.value, this.valueBefore);
47316             this.valueBefore = this.value;
47317         }
47318         
47319     },
47320     
47321     setValue : function(v){
47322         // Roo.log(v);
47323         this.value = v;
47324         
47325         var vals = this.getValueArray();
47326         var tv = [];
47327         Roo.each(vals, function(k) {
47328             var r = this.findRecord(this.valueField, k);
47329             if(r){
47330                 tv.push(r.data[this.displayField]);
47331             }else if(this.valueNotFoundText !== undefined){
47332                 tv.push( this.valueNotFoundText );
47333             }
47334         },this);
47335        // Roo.log(tv);
47336         
47337         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47338         this.hiddenField.value = v;
47339         this.value = v;
47340     }
47341     
47342 });/*
47343  * Based on:
47344  * Ext JS Library 1.1.1
47345  * Copyright(c) 2006-2007, Ext JS, LLC.
47346  *
47347  * Originally Released Under LGPL - original licence link has changed is not relivant.
47348  *
47349  * Fork - LGPL
47350  * <script type="text/javascript">
47351  */
47352  
47353 /**
47354  * @class Roo.form.Signature
47355  * @extends Roo.form.Field
47356  * Signature field.  
47357  * @constructor
47358  * 
47359  * @param {Object} config Configuration options
47360  */
47361
47362 Roo.form.Signature = function(config){
47363     Roo.form.Signature.superclass.constructor.call(this, config);
47364     
47365     this.addEvents({// not in used??
47366          /**
47367          * @event confirm
47368          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47369              * @param {Roo.form.Signature} combo This combo box
47370              */
47371         'confirm' : true,
47372         /**
47373          * @event reset
47374          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47375              * @param {Roo.form.ComboBox} combo This combo box
47376              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47377              */
47378         'reset' : true
47379     });
47380 };
47381
47382 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47383     /**
47384      * @cfg {Object} labels Label to use when rendering a form.
47385      * defaults to 
47386      * labels : { 
47387      *      clear : "Clear",
47388      *      confirm : "Confirm"
47389      *  }
47390      */
47391     labels : { 
47392         clear : "Clear",
47393         confirm : "Confirm"
47394     },
47395     /**
47396      * @cfg {Number} width The signature panel width (defaults to 300)
47397      */
47398     width: 300,
47399     /**
47400      * @cfg {Number} height The signature panel height (defaults to 100)
47401      */
47402     height : 100,
47403     /**
47404      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47405      */
47406     allowBlank : false,
47407     
47408     //private
47409     // {Object} signPanel The signature SVG panel element (defaults to {})
47410     signPanel : {},
47411     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47412     isMouseDown : false,
47413     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47414     isConfirmed : false,
47415     // {String} signatureTmp SVG mapping string (defaults to empty string)
47416     signatureTmp : '',
47417     
47418     
47419     defaultAutoCreate : { // modified by initCompnoent..
47420         tag: "input",
47421         type:"hidden"
47422     },
47423
47424     // private
47425     onRender : function(ct, position){
47426         
47427         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47428         
47429         this.wrap = this.el.wrap({
47430             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47431         });
47432         
47433         this.createToolbar(this);
47434         this.signPanel = this.wrap.createChild({
47435                 tag: 'div',
47436                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47437             }, this.el
47438         );
47439             
47440         this.svgID = Roo.id();
47441         this.svgEl = this.signPanel.createChild({
47442               xmlns : 'http://www.w3.org/2000/svg',
47443               tag : 'svg',
47444               id : this.svgID + "-svg",
47445               width: this.width,
47446               height: this.height,
47447               viewBox: '0 0 '+this.width+' '+this.height,
47448               cn : [
47449                 {
47450                     tag: "rect",
47451                     id: this.svgID + "-svg-r",
47452                     width: this.width,
47453                     height: this.height,
47454                     fill: "#ffa"
47455                 },
47456                 {
47457                     tag: "line",
47458                     id: this.svgID + "-svg-l",
47459                     x1: "0", // start
47460                     y1: (this.height*0.8), // start set the line in 80% of height
47461                     x2: this.width, // end
47462                     y2: (this.height*0.8), // end set the line in 80% of height
47463                     'stroke': "#666",
47464                     'stroke-width': "1",
47465                     'stroke-dasharray': "3",
47466                     'shape-rendering': "crispEdges",
47467                     'pointer-events': "none"
47468                 },
47469                 {
47470                     tag: "path",
47471                     id: this.svgID + "-svg-p",
47472                     'stroke': "navy",
47473                     'stroke-width': "3",
47474                     'fill': "none",
47475                     'pointer-events': 'none'
47476                 }
47477               ]
47478         });
47479         this.createSVG();
47480         this.svgBox = this.svgEl.dom.getScreenCTM();
47481     },
47482     createSVG : function(){ 
47483         var svg = this.signPanel;
47484         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47485         var t = this;
47486
47487         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47488         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47489         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47490         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47491         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47492         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47493         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47494         
47495     },
47496     isTouchEvent : function(e){
47497         return e.type.match(/^touch/);
47498     },
47499     getCoords : function (e) {
47500         var pt    = this.svgEl.dom.createSVGPoint();
47501         pt.x = e.clientX; 
47502         pt.y = e.clientY;
47503         if (this.isTouchEvent(e)) {
47504             pt.x =  e.targetTouches[0].clientX 
47505             pt.y = e.targetTouches[0].clientY;
47506         }
47507         var a = this.svgEl.dom.getScreenCTM();
47508         var b = a.inverse();
47509         var mx = pt.matrixTransform(b);
47510         return mx.x + ',' + mx.y;
47511     },
47512     //mouse event headler 
47513     down : function (e) {
47514         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47515         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47516         
47517         this.isMouseDown = true;
47518         
47519         e.preventDefault();
47520     },
47521     move : function (e) {
47522         if (this.isMouseDown) {
47523             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47524             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47525         }
47526         
47527         e.preventDefault();
47528     },
47529     up : function (e) {
47530         this.isMouseDown = false;
47531         var sp = this.signatureTmp.split(' ');
47532         
47533         if(sp.length > 1){
47534             if(!sp[sp.length-2].match(/^L/)){
47535                 sp.pop();
47536                 sp.pop();
47537                 sp.push("");
47538                 this.signatureTmp = sp.join(" ");
47539             }
47540         }
47541         if(this.getValue() != this.signatureTmp){
47542             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47543             this.isConfirmed = false;
47544         }
47545         e.preventDefault();
47546     },
47547     
47548     /**
47549      * Protected method that will not generally be called directly. It
47550      * is called when the editor creates its toolbar. Override this method if you need to
47551      * add custom toolbar buttons.
47552      * @param {HtmlEditor} editor
47553      */
47554     createToolbar : function(editor){
47555          function btn(id, toggle, handler){
47556             var xid = fid + '-'+ id ;
47557             return {
47558                 id : xid,
47559                 cmd : id,
47560                 cls : 'x-btn-icon x-edit-'+id,
47561                 enableToggle:toggle !== false,
47562                 scope: editor, // was editor...
47563                 handler:handler||editor.relayBtnCmd,
47564                 clickEvent:'mousedown',
47565                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47566                 tabIndex:-1
47567             };
47568         }
47569         
47570         
47571         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47572         this.tb = tb;
47573         this.tb.add(
47574            {
47575                 cls : ' x-signature-btn x-signature-'+id,
47576                 scope: editor, // was editor...
47577                 handler: this.reset,
47578                 clickEvent:'mousedown',
47579                 text: this.labels.clear
47580             },
47581             {
47582                  xtype : 'Fill',
47583                  xns: Roo.Toolbar
47584             }, 
47585             {
47586                 cls : '  x-signature-btn x-signature-'+id,
47587                 scope: editor, // was editor...
47588                 handler: this.confirmHandler,
47589                 clickEvent:'mousedown',
47590                 text: this.labels.confirm
47591             }
47592         );
47593     
47594     },
47595     //public
47596     /**
47597      * when user is clicked confirm then show this image.....
47598      * 
47599      * @return {String} Image Data URI
47600      */
47601     getImageDataURI : function(){
47602         var svg = this.svgEl.dom.parentNode.innerHTML;
47603         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47604         return src; 
47605     },
47606     /**
47607      * 
47608      * @return {Boolean} this.isConfirmed
47609      */
47610     getConfirmed : function(){
47611         return this.isConfirmed;
47612     },
47613     /**
47614      * 
47615      * @return {Number} this.width
47616      */
47617     getWidth : function(){
47618         return this.width;
47619     },
47620     /**
47621      * 
47622      * @return {Number} this.height
47623      */
47624     getHeight : function(){
47625         return this.height;
47626     },
47627     // private
47628     getSignature : function(){
47629         return this.signatureTmp;
47630     },
47631     // private
47632     reset : function(){
47633         this.signatureTmp = '';
47634         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47635         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47636         this.isConfirmed = false;
47637         Roo.form.Signature.superclass.reset.call(this);
47638     },
47639     setSignature : function(s){
47640         this.signatureTmp = s;
47641         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47642         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47643         this.setValue(s);
47644         this.isConfirmed = false;
47645         Roo.form.Signature.superclass.reset.call(this);
47646     }, 
47647     test : function(){
47648 //        Roo.log(this.signPanel.dom.contentWindow.up())
47649     },
47650     //private
47651     setConfirmed : function(){
47652         
47653         
47654         
47655 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47656     },
47657     // private
47658     confirmHandler : function(){
47659         if(!this.getSignature()){
47660             return;
47661         }
47662         
47663         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47664         this.setValue(this.getSignature());
47665         this.isConfirmed = true;
47666         
47667         this.fireEvent('confirm', this);
47668     },
47669     // private
47670     // Subclasses should provide the validation implementation by overriding this
47671     validateValue : function(value){
47672         if(this.allowBlank){
47673             return true;
47674         }
47675         
47676         if(this.isConfirmed){
47677             return true;
47678         }
47679         return false;
47680     }
47681 });/*
47682  * Based on:
47683  * Ext JS Library 1.1.1
47684  * Copyright(c) 2006-2007, Ext JS, LLC.
47685  *
47686  * Originally Released Under LGPL - original licence link has changed is not relivant.
47687  *
47688  * Fork - LGPL
47689  * <script type="text/javascript">
47690  */
47691  
47692
47693 /**
47694  * @class Roo.form.ComboBox
47695  * @extends Roo.form.TriggerField
47696  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47697  * @constructor
47698  * Create a new ComboBox.
47699  * @param {Object} config Configuration options
47700  */
47701 Roo.form.Select = function(config){
47702     Roo.form.Select.superclass.constructor.call(this, config);
47703      
47704 };
47705
47706 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47707     /**
47708      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47709      */
47710     /**
47711      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47712      * rendering into an Roo.Editor, defaults to false)
47713      */
47714     /**
47715      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47716      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47717      */
47718     /**
47719      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47720      */
47721     /**
47722      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47723      * the dropdown list (defaults to undefined, with no header element)
47724      */
47725
47726      /**
47727      * @cfg {String/Roo.Template} tpl The template to use to render the output
47728      */
47729      
47730     // private
47731     defaultAutoCreate : {tag: "select"  },
47732     /**
47733      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47734      */
47735     listWidth: undefined,
47736     /**
47737      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47738      * mode = 'remote' or 'text' if mode = 'local')
47739      */
47740     displayField: undefined,
47741     /**
47742      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47743      * mode = 'remote' or 'value' if mode = 'local'). 
47744      * Note: use of a valueField requires the user make a selection
47745      * in order for a value to be mapped.
47746      */
47747     valueField: undefined,
47748     
47749     
47750     /**
47751      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47752      * field's data value (defaults to the underlying DOM element's name)
47753      */
47754     hiddenName: undefined,
47755     /**
47756      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47757      */
47758     listClass: '',
47759     /**
47760      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47761      */
47762     selectedClass: 'x-combo-selected',
47763     /**
47764      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47765      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47766      * which displays a downward arrow icon).
47767      */
47768     triggerClass : 'x-form-arrow-trigger',
47769     /**
47770      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47771      */
47772     shadow:'sides',
47773     /**
47774      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47775      * anchor positions (defaults to 'tl-bl')
47776      */
47777     listAlign: 'tl-bl?',
47778     /**
47779      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47780      */
47781     maxHeight: 300,
47782     /**
47783      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47784      * query specified by the allQuery config option (defaults to 'query')
47785      */
47786     triggerAction: 'query',
47787     /**
47788      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47789      * (defaults to 4, does not apply if editable = false)
47790      */
47791     minChars : 4,
47792     /**
47793      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47794      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47795      */
47796     typeAhead: false,
47797     /**
47798      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47799      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47800      */
47801     queryDelay: 500,
47802     /**
47803      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47804      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47805      */
47806     pageSize: 0,
47807     /**
47808      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47809      * when editable = true (defaults to false)
47810      */
47811     selectOnFocus:false,
47812     /**
47813      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47814      */
47815     queryParam: 'query',
47816     /**
47817      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47818      * when mode = 'remote' (defaults to 'Loading...')
47819      */
47820     loadingText: 'Loading...',
47821     /**
47822      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47823      */
47824     resizable: false,
47825     /**
47826      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47827      */
47828     handleHeight : 8,
47829     /**
47830      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47831      * traditional select (defaults to true)
47832      */
47833     editable: true,
47834     /**
47835      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47836      */
47837     allQuery: '',
47838     /**
47839      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47840      */
47841     mode: 'remote',
47842     /**
47843      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47844      * listWidth has a higher value)
47845      */
47846     minListWidth : 70,
47847     /**
47848      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47849      * allow the user to set arbitrary text into the field (defaults to false)
47850      */
47851     forceSelection:false,
47852     /**
47853      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47854      * if typeAhead = true (defaults to 250)
47855      */
47856     typeAheadDelay : 250,
47857     /**
47858      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47859      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47860      */
47861     valueNotFoundText : undefined,
47862     
47863     /**
47864      * @cfg {String} defaultValue The value displayed after loading the store.
47865      */
47866     defaultValue: '',
47867     
47868     /**
47869      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47870      */
47871     blockFocus : false,
47872     
47873     /**
47874      * @cfg {Boolean} disableClear Disable showing of clear button.
47875      */
47876     disableClear : false,
47877     /**
47878      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47879      */
47880     alwaysQuery : false,
47881     
47882     //private
47883     addicon : false,
47884     editicon: false,
47885     
47886     // element that contains real text value.. (when hidden is used..)
47887      
47888     // private
47889     onRender : function(ct, position){
47890         Roo.form.Field.prototype.onRender.call(this, ct, position);
47891         
47892         if(this.store){
47893             this.store.on('beforeload', this.onBeforeLoad, this);
47894             this.store.on('load', this.onLoad, this);
47895             this.store.on('loadexception', this.onLoadException, this);
47896             this.store.load({});
47897         }
47898         
47899         
47900         
47901     },
47902
47903     // private
47904     initEvents : function(){
47905         //Roo.form.ComboBox.superclass.initEvents.call(this);
47906  
47907     },
47908
47909     onDestroy : function(){
47910        
47911         if(this.store){
47912             this.store.un('beforeload', this.onBeforeLoad, this);
47913             this.store.un('load', this.onLoad, this);
47914             this.store.un('loadexception', this.onLoadException, this);
47915         }
47916         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47917     },
47918
47919     // private
47920     fireKey : function(e){
47921         if(e.isNavKeyPress() && !this.list.isVisible()){
47922             this.fireEvent("specialkey", this, e);
47923         }
47924     },
47925
47926     // private
47927     onResize: function(w, h){
47928         
47929         return; 
47930     
47931         
47932     },
47933
47934     /**
47935      * Allow or prevent the user from directly editing the field text.  If false is passed,
47936      * the user will only be able to select from the items defined in the dropdown list.  This method
47937      * is the runtime equivalent of setting the 'editable' config option at config time.
47938      * @param {Boolean} value True to allow the user to directly edit the field text
47939      */
47940     setEditable : function(value){
47941          
47942     },
47943
47944     // private
47945     onBeforeLoad : function(){
47946         
47947         Roo.log("Select before load");
47948         return;
47949     
47950         this.innerList.update(this.loadingText ?
47951                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47952         //this.restrictHeight();
47953         this.selectedIndex = -1;
47954     },
47955
47956     // private
47957     onLoad : function(){
47958
47959     
47960         var dom = this.el.dom;
47961         dom.innerHTML = '';
47962          var od = dom.ownerDocument;
47963          
47964         if (this.emptyText) {
47965             var op = od.createElement('option');
47966             op.setAttribute('value', '');
47967             op.innerHTML = String.format('{0}', this.emptyText);
47968             dom.appendChild(op);
47969         }
47970         if(this.store.getCount() > 0){
47971            
47972             var vf = this.valueField;
47973             var df = this.displayField;
47974             this.store.data.each(function(r) {
47975                 // which colmsn to use... testing - cdoe / title..
47976                 var op = od.createElement('option');
47977                 op.setAttribute('value', r.data[vf]);
47978                 op.innerHTML = String.format('{0}', r.data[df]);
47979                 dom.appendChild(op);
47980             });
47981             if (typeof(this.defaultValue != 'undefined')) {
47982                 this.setValue(this.defaultValue);
47983             }
47984             
47985              
47986         }else{
47987             //this.onEmptyResults();
47988         }
47989         //this.el.focus();
47990     },
47991     // private
47992     onLoadException : function()
47993     {
47994         dom.innerHTML = '';
47995             
47996         Roo.log("Select on load exception");
47997         return;
47998     
47999         this.collapse();
48000         Roo.log(this.store.reader.jsonData);
48001         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48002             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48003         }
48004         
48005         
48006     },
48007     // private
48008     onTypeAhead : function(){
48009          
48010     },
48011
48012     // private
48013     onSelect : function(record, index){
48014         Roo.log('on select?');
48015         return;
48016         if(this.fireEvent('beforeselect', this, record, index) !== false){
48017             this.setFromData(index > -1 ? record.data : false);
48018             this.collapse();
48019             this.fireEvent('select', this, record, index);
48020         }
48021     },
48022
48023     /**
48024      * Returns the currently selected field value or empty string if no value is set.
48025      * @return {String} value The selected value
48026      */
48027     getValue : function(){
48028         var dom = this.el.dom;
48029         this.value = dom.options[dom.selectedIndex].value;
48030         return this.value;
48031         
48032     },
48033
48034     /**
48035      * Clears any text/value currently set in the field
48036      */
48037     clearValue : function(){
48038         this.value = '';
48039         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48040         
48041     },
48042
48043     /**
48044      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48045      * will be displayed in the field.  If the value does not match the data value of an existing item,
48046      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48047      * Otherwise the field will be blank (although the value will still be set).
48048      * @param {String} value The value to match
48049      */
48050     setValue : function(v){
48051         var d = this.el.dom;
48052         for (var i =0; i < d.options.length;i++) {
48053             if (v == d.options[i].value) {
48054                 d.selectedIndex = i;
48055                 this.value = v;
48056                 return;
48057             }
48058         }
48059         this.clearValue();
48060     },
48061     /**
48062      * @property {Object} the last set data for the element
48063      */
48064     
48065     lastData : false,
48066     /**
48067      * Sets the value of the field based on a object which is related to the record format for the store.
48068      * @param {Object} value the value to set as. or false on reset?
48069      */
48070     setFromData : function(o){
48071         Roo.log('setfrom data?');
48072          
48073         
48074         
48075     },
48076     // private
48077     reset : function(){
48078         this.clearValue();
48079     },
48080     // private
48081     findRecord : function(prop, value){
48082         
48083         return false;
48084     
48085         var record;
48086         if(this.store.getCount() > 0){
48087             this.store.each(function(r){
48088                 if(r.data[prop] == value){
48089                     record = r;
48090                     return false;
48091                 }
48092                 return true;
48093             });
48094         }
48095         return record;
48096     },
48097     
48098     getName: function()
48099     {
48100         // returns hidden if it's set..
48101         if (!this.rendered) {return ''};
48102         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48103         
48104     },
48105      
48106
48107     
48108
48109     // private
48110     onEmptyResults : function(){
48111         Roo.log('empty results');
48112         //this.collapse();
48113     },
48114
48115     /**
48116      * Returns true if the dropdown list is expanded, else false.
48117      */
48118     isExpanded : function(){
48119         return false;
48120     },
48121
48122     /**
48123      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48124      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48125      * @param {String} value The data value of the item to select
48126      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48127      * selected item if it is not currently in view (defaults to true)
48128      * @return {Boolean} True if the value matched an item in the list, else false
48129      */
48130     selectByValue : function(v, scrollIntoView){
48131         Roo.log('select By Value');
48132         return false;
48133     
48134         if(v !== undefined && v !== null){
48135             var r = this.findRecord(this.valueField || this.displayField, v);
48136             if(r){
48137                 this.select(this.store.indexOf(r), scrollIntoView);
48138                 return true;
48139             }
48140         }
48141         return false;
48142     },
48143
48144     /**
48145      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48146      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48147      * @param {Number} index The zero-based index of the list item to select
48148      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48149      * selected item if it is not currently in view (defaults to true)
48150      */
48151     select : function(index, scrollIntoView){
48152         Roo.log('select ');
48153         return  ;
48154         
48155         this.selectedIndex = index;
48156         this.view.select(index);
48157         if(scrollIntoView !== false){
48158             var el = this.view.getNode(index);
48159             if(el){
48160                 this.innerList.scrollChildIntoView(el, false);
48161             }
48162         }
48163     },
48164
48165       
48166
48167     // private
48168     validateBlur : function(){
48169         
48170         return;
48171         
48172     },
48173
48174     // private
48175     initQuery : function(){
48176         this.doQuery(this.getRawValue());
48177     },
48178
48179     // private
48180     doForce : function(){
48181         if(this.el.dom.value.length > 0){
48182             this.el.dom.value =
48183                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48184              
48185         }
48186     },
48187
48188     /**
48189      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48190      * query allowing the query action to be canceled if needed.
48191      * @param {String} query The SQL query to execute
48192      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48193      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48194      * saved in the current store (defaults to false)
48195      */
48196     doQuery : function(q, forceAll){
48197         
48198         Roo.log('doQuery?');
48199         if(q === undefined || q === null){
48200             q = '';
48201         }
48202         var qe = {
48203             query: q,
48204             forceAll: forceAll,
48205             combo: this,
48206             cancel:false
48207         };
48208         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48209             return false;
48210         }
48211         q = qe.query;
48212         forceAll = qe.forceAll;
48213         if(forceAll === true || (q.length >= this.minChars)){
48214             if(this.lastQuery != q || this.alwaysQuery){
48215                 this.lastQuery = q;
48216                 if(this.mode == 'local'){
48217                     this.selectedIndex = -1;
48218                     if(forceAll){
48219                         this.store.clearFilter();
48220                     }else{
48221                         this.store.filter(this.displayField, q);
48222                     }
48223                     this.onLoad();
48224                 }else{
48225                     this.store.baseParams[this.queryParam] = q;
48226                     this.store.load({
48227                         params: this.getParams(q)
48228                     });
48229                     this.expand();
48230                 }
48231             }else{
48232                 this.selectedIndex = -1;
48233                 this.onLoad();   
48234             }
48235         }
48236     },
48237
48238     // private
48239     getParams : function(q){
48240         var p = {};
48241         //p[this.queryParam] = q;
48242         if(this.pageSize){
48243             p.start = 0;
48244             p.limit = this.pageSize;
48245         }
48246         return p;
48247     },
48248
48249     /**
48250      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48251      */
48252     collapse : function(){
48253         
48254     },
48255
48256     // private
48257     collapseIf : function(e){
48258         
48259     },
48260
48261     /**
48262      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48263      */
48264     expand : function(){
48265         
48266     } ,
48267
48268     // private
48269      
48270
48271     /** 
48272     * @cfg {Boolean} grow 
48273     * @hide 
48274     */
48275     /** 
48276     * @cfg {Number} growMin 
48277     * @hide 
48278     */
48279     /** 
48280     * @cfg {Number} growMax 
48281     * @hide 
48282     */
48283     /**
48284      * @hide
48285      * @method autoSize
48286      */
48287     
48288     setWidth : function()
48289     {
48290         
48291     },
48292     getResizeEl : function(){
48293         return this.el;
48294     }
48295 });//<script type="text/javasscript">
48296  
48297
48298 /**
48299  * @class Roo.DDView
48300  * A DnD enabled version of Roo.View.
48301  * @param {Element/String} container The Element in which to create the View.
48302  * @param {String} tpl The template string used to create the markup for each element of the View
48303  * @param {Object} config The configuration properties. These include all the config options of
48304  * {@link Roo.View} plus some specific to this class.<br>
48305  * <p>
48306  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48307  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48308  * <p>
48309  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48310 .x-view-drag-insert-above {
48311         border-top:1px dotted #3366cc;
48312 }
48313 .x-view-drag-insert-below {
48314         border-bottom:1px dotted #3366cc;
48315 }
48316 </code></pre>
48317  * 
48318  */
48319  
48320 Roo.DDView = function(container, tpl, config) {
48321     Roo.DDView.superclass.constructor.apply(this, arguments);
48322     this.getEl().setStyle("outline", "0px none");
48323     this.getEl().unselectable();
48324     if (this.dragGroup) {
48325                 this.setDraggable(this.dragGroup.split(","));
48326     }
48327     if (this.dropGroup) {
48328                 this.setDroppable(this.dropGroup.split(","));
48329     }
48330     if (this.deletable) {
48331         this.setDeletable();
48332     }
48333     this.isDirtyFlag = false;
48334         this.addEvents({
48335                 "drop" : true
48336         });
48337 };
48338
48339 Roo.extend(Roo.DDView, Roo.View, {
48340 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48341 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48342 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48343 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48344
48345         isFormField: true,
48346
48347         reset: Roo.emptyFn,
48348         
48349         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48350
48351         validate: function() {
48352                 return true;
48353         },
48354         
48355         destroy: function() {
48356                 this.purgeListeners();
48357                 this.getEl.removeAllListeners();
48358                 this.getEl().remove();
48359                 if (this.dragZone) {
48360                         if (this.dragZone.destroy) {
48361                                 this.dragZone.destroy();
48362                         }
48363                 }
48364                 if (this.dropZone) {
48365                         if (this.dropZone.destroy) {
48366                                 this.dropZone.destroy();
48367                         }
48368                 }
48369         },
48370
48371 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48372         getName: function() {
48373                 return this.name;
48374         },
48375
48376 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48377         setValue: function(v) {
48378                 if (!this.store) {
48379                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48380                 }
48381                 var data = {};
48382                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48383                 this.store.proxy = new Roo.data.MemoryProxy(data);
48384                 this.store.load();
48385         },
48386
48387 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48388         getValue: function() {
48389                 var result = '(';
48390                 this.store.each(function(rec) {
48391                         result += rec.id + ',';
48392                 });
48393                 return result.substr(0, result.length - 1) + ')';
48394         },
48395         
48396         getIds: function() {
48397                 var i = 0, result = new Array(this.store.getCount());
48398                 this.store.each(function(rec) {
48399                         result[i++] = rec.id;
48400                 });
48401                 return result;
48402         },
48403         
48404         isDirty: function() {
48405                 return this.isDirtyFlag;
48406         },
48407
48408 /**
48409  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48410  *      whole Element becomes the target, and this causes the drop gesture to append.
48411  */
48412     getTargetFromEvent : function(e) {
48413                 var target = e.getTarget();
48414                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48415                 target = target.parentNode;
48416                 }
48417                 if (!target) {
48418                         target = this.el.dom.lastChild || this.el.dom;
48419                 }
48420                 return target;
48421     },
48422
48423 /**
48424  *      Create the drag data which consists of an object which has the property "ddel" as
48425  *      the drag proxy element. 
48426  */
48427     getDragData : function(e) {
48428         var target = this.findItemFromChild(e.getTarget());
48429                 if(target) {
48430                         this.handleSelection(e);
48431                         var selNodes = this.getSelectedNodes();
48432             var dragData = {
48433                 source: this,
48434                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48435                 nodes: selNodes,
48436                 records: []
48437                         };
48438                         var selectedIndices = this.getSelectedIndexes();
48439                         for (var i = 0; i < selectedIndices.length; i++) {
48440                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48441                         }
48442                         if (selNodes.length == 1) {
48443                                 dragData.ddel = target.cloneNode(true); // the div element
48444                         } else {
48445                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48446                                 div.className = 'multi-proxy';
48447                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48448                                         div.appendChild(selNodes[i].cloneNode(true));
48449                                 }
48450                                 dragData.ddel = div;
48451                         }
48452             //console.log(dragData)
48453             //console.log(dragData.ddel.innerHTML)
48454                         return dragData;
48455                 }
48456         //console.log('nodragData')
48457                 return false;
48458     },
48459     
48460 /**     Specify to which ddGroup items in this DDView may be dragged. */
48461     setDraggable: function(ddGroup) {
48462         if (ddGroup instanceof Array) {
48463                 Roo.each(ddGroup, this.setDraggable, this);
48464                 return;
48465         }
48466         if (this.dragZone) {
48467                 this.dragZone.addToGroup(ddGroup);
48468         } else {
48469                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48470                                 containerScroll: true,
48471                                 ddGroup: ddGroup 
48472
48473                         });
48474 //                      Draggability implies selection. DragZone's mousedown selects the element.
48475                         if (!this.multiSelect) { this.singleSelect = true; }
48476
48477 //                      Wire the DragZone's handlers up to methods in *this*
48478                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48479                 }
48480     },
48481
48482 /**     Specify from which ddGroup this DDView accepts drops. */
48483     setDroppable: function(ddGroup) {
48484         if (ddGroup instanceof Array) {
48485                 Roo.each(ddGroup, this.setDroppable, this);
48486                 return;
48487         }
48488         if (this.dropZone) {
48489                 this.dropZone.addToGroup(ddGroup);
48490         } else {
48491                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48492                                 containerScroll: true,
48493                                 ddGroup: ddGroup
48494                         });
48495
48496 //                      Wire the DropZone's handlers up to methods in *this*
48497                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48498                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48499                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48500                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48501                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48502                 }
48503     },
48504
48505 /**     Decide whether to drop above or below a View node. */
48506     getDropPoint : function(e, n, dd){
48507         if (n == this.el.dom) { return "above"; }
48508                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48509                 var c = t + (b - t) / 2;
48510                 var y = Roo.lib.Event.getPageY(e);
48511                 if(y <= c) {
48512                         return "above";
48513                 }else{
48514                         return "below";
48515                 }
48516     },
48517
48518     onNodeEnter : function(n, dd, e, data){
48519                 return false;
48520     },
48521     
48522     onNodeOver : function(n, dd, e, data){
48523                 var pt = this.getDropPoint(e, n, dd);
48524                 // set the insert point style on the target node
48525                 var dragElClass = this.dropNotAllowed;
48526                 if (pt) {
48527                         var targetElClass;
48528                         if (pt == "above"){
48529                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48530                                 targetElClass = "x-view-drag-insert-above";
48531                         } else {
48532                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48533                                 targetElClass = "x-view-drag-insert-below";
48534                         }
48535                         if (this.lastInsertClass != targetElClass){
48536                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48537                                 this.lastInsertClass = targetElClass;
48538                         }
48539                 }
48540                 return dragElClass;
48541         },
48542
48543     onNodeOut : function(n, dd, e, data){
48544                 this.removeDropIndicators(n);
48545     },
48546
48547     onNodeDrop : function(n, dd, e, data){
48548         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48549                 return false;
48550         }
48551         var pt = this.getDropPoint(e, n, dd);
48552                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48553                 if (pt == "below") { insertAt++; }
48554                 for (var i = 0; i < data.records.length; i++) {
48555                         var r = data.records[i];
48556                         var dup = this.store.getById(r.id);
48557                         if (dup && (dd != this.dragZone)) {
48558                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48559                         } else {
48560                                 if (data.copy) {
48561                                         this.store.insert(insertAt++, r.copy());
48562                                 } else {
48563                                         data.source.isDirtyFlag = true;
48564                                         r.store.remove(r);
48565                                         this.store.insert(insertAt++, r);
48566                                 }
48567                                 this.isDirtyFlag = true;
48568                         }
48569                 }
48570                 this.dragZone.cachedTarget = null;
48571                 return true;
48572     },
48573
48574     removeDropIndicators : function(n){
48575                 if(n){
48576                         Roo.fly(n).removeClass([
48577                                 "x-view-drag-insert-above",
48578                                 "x-view-drag-insert-below"]);
48579                         this.lastInsertClass = "_noclass";
48580                 }
48581     },
48582
48583 /**
48584  *      Utility method. Add a delete option to the DDView's context menu.
48585  *      @param {String} imageUrl The URL of the "delete" icon image.
48586  */
48587         setDeletable: function(imageUrl) {
48588                 if (!this.singleSelect && !this.multiSelect) {
48589                         this.singleSelect = true;
48590                 }
48591                 var c = this.getContextMenu();
48592                 this.contextMenu.on("itemclick", function(item) {
48593                         switch (item.id) {
48594                                 case "delete":
48595                                         this.remove(this.getSelectedIndexes());
48596                                         break;
48597                         }
48598                 }, this);
48599                 this.contextMenu.add({
48600                         icon: imageUrl,
48601                         id: "delete",
48602                         text: 'Delete'
48603                 });
48604         },
48605         
48606 /**     Return the context menu for this DDView. */
48607         getContextMenu: function() {
48608                 if (!this.contextMenu) {
48609 //                      Create the View's context menu
48610                         this.contextMenu = new Roo.menu.Menu({
48611                                 id: this.id + "-contextmenu"
48612                         });
48613                         this.el.on("contextmenu", this.showContextMenu, this);
48614                 }
48615                 return this.contextMenu;
48616         },
48617         
48618         disableContextMenu: function() {
48619                 if (this.contextMenu) {
48620                         this.el.un("contextmenu", this.showContextMenu, this);
48621                 }
48622         },
48623
48624         showContextMenu: function(e, item) {
48625         item = this.findItemFromChild(e.getTarget());
48626                 if (item) {
48627                         e.stopEvent();
48628                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48629                         this.contextMenu.showAt(e.getXY());
48630             }
48631     },
48632
48633 /**
48634  *      Remove {@link Roo.data.Record}s at the specified indices.
48635  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48636  */
48637     remove: function(selectedIndices) {
48638                 selectedIndices = [].concat(selectedIndices);
48639                 for (var i = 0; i < selectedIndices.length; i++) {
48640                         var rec = this.store.getAt(selectedIndices[i]);
48641                         this.store.remove(rec);
48642                 }
48643     },
48644
48645 /**
48646  *      Double click fires the event, but also, if this is draggable, and there is only one other
48647  *      related DropZone, it transfers the selected node.
48648  */
48649     onDblClick : function(e){
48650         var item = this.findItemFromChild(e.getTarget());
48651         if(item){
48652             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48653                 return false;
48654             }
48655             if (this.dragGroup) {
48656                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48657                     while (targets.indexOf(this.dropZone) > -1) {
48658                             targets.remove(this.dropZone);
48659                                 }
48660                     if (targets.length == 1) {
48661                                         this.dragZone.cachedTarget = null;
48662                         var el = Roo.get(targets[0].getEl());
48663                         var box = el.getBox(true);
48664                         targets[0].onNodeDrop(el.dom, {
48665                                 target: el.dom,
48666                                 xy: [box.x, box.y + box.height - 1]
48667                         }, null, this.getDragData(e));
48668                     }
48669                 }
48670         }
48671     },
48672     
48673     handleSelection: function(e) {
48674                 this.dragZone.cachedTarget = null;
48675         var item = this.findItemFromChild(e.getTarget());
48676         if (!item) {
48677                 this.clearSelections(true);
48678                 return;
48679         }
48680                 if (item && (this.multiSelect || this.singleSelect)){
48681                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48682                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48683                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48684                                 this.unselect(item);
48685                         } else {
48686                                 this.select(item, this.multiSelect && e.ctrlKey);
48687                                 this.lastSelection = item;
48688                         }
48689                 }
48690     },
48691
48692     onItemClick : function(item, index, e){
48693                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48694                         return false;
48695                 }
48696                 return true;
48697     },
48698
48699     unselect : function(nodeInfo, suppressEvent){
48700                 var node = this.getNode(nodeInfo);
48701                 if(node && this.isSelected(node)){
48702                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48703                                 Roo.fly(node).removeClass(this.selectedClass);
48704                                 this.selections.remove(node);
48705                                 if(!suppressEvent){
48706                                         this.fireEvent("selectionchange", this, this.selections);
48707                                 }
48708                         }
48709                 }
48710     }
48711 });
48712 /*
48713  * Based on:
48714  * Ext JS Library 1.1.1
48715  * Copyright(c) 2006-2007, Ext JS, LLC.
48716  *
48717  * Originally Released Under LGPL - original licence link has changed is not relivant.
48718  *
48719  * Fork - LGPL
48720  * <script type="text/javascript">
48721  */
48722  
48723 /**
48724  * @class Roo.LayoutManager
48725  * @extends Roo.util.Observable
48726  * Base class for layout managers.
48727  */
48728 Roo.LayoutManager = function(container, config){
48729     Roo.LayoutManager.superclass.constructor.call(this);
48730     this.el = Roo.get(container);
48731     // ie scrollbar fix
48732     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48733         document.body.scroll = "no";
48734     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48735         this.el.position('relative');
48736     }
48737     this.id = this.el.id;
48738     this.el.addClass("x-layout-container");
48739     /** false to disable window resize monitoring @type Boolean */
48740     this.monitorWindowResize = true;
48741     this.regions = {};
48742     this.addEvents({
48743         /**
48744          * @event layout
48745          * Fires when a layout is performed. 
48746          * @param {Roo.LayoutManager} this
48747          */
48748         "layout" : true,
48749         /**
48750          * @event regionresized
48751          * Fires when the user resizes a region. 
48752          * @param {Roo.LayoutRegion} region The resized region
48753          * @param {Number} newSize The new size (width for east/west, height for north/south)
48754          */
48755         "regionresized" : true,
48756         /**
48757          * @event regioncollapsed
48758          * Fires when a region is collapsed. 
48759          * @param {Roo.LayoutRegion} region The collapsed region
48760          */
48761         "regioncollapsed" : true,
48762         /**
48763          * @event regionexpanded
48764          * Fires when a region is expanded.  
48765          * @param {Roo.LayoutRegion} region The expanded region
48766          */
48767         "regionexpanded" : true
48768     });
48769     this.updating = false;
48770     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48771 };
48772
48773 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48774     /**
48775      * Returns true if this layout is currently being updated
48776      * @return {Boolean}
48777      */
48778     isUpdating : function(){
48779         return this.updating; 
48780     },
48781     
48782     /**
48783      * Suspend the LayoutManager from doing auto-layouts while
48784      * making multiple add or remove calls
48785      */
48786     beginUpdate : function(){
48787         this.updating = true;    
48788     },
48789     
48790     /**
48791      * Restore auto-layouts and optionally disable the manager from performing a layout
48792      * @param {Boolean} noLayout true to disable a layout update 
48793      */
48794     endUpdate : function(noLayout){
48795         this.updating = false;
48796         if(!noLayout){
48797             this.layout();
48798         }    
48799     },
48800     
48801     layout: function(){
48802         
48803     },
48804     
48805     onRegionResized : function(region, newSize){
48806         this.fireEvent("regionresized", region, newSize);
48807         this.layout();
48808     },
48809     
48810     onRegionCollapsed : function(region){
48811         this.fireEvent("regioncollapsed", region);
48812     },
48813     
48814     onRegionExpanded : function(region){
48815         this.fireEvent("regionexpanded", region);
48816     },
48817         
48818     /**
48819      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48820      * performs box-model adjustments.
48821      * @return {Object} The size as an object {width: (the width), height: (the height)}
48822      */
48823     getViewSize : function(){
48824         var size;
48825         if(this.el.dom != document.body){
48826             size = this.el.getSize();
48827         }else{
48828             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48829         }
48830         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48831         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48832         return size;
48833     },
48834     
48835     /**
48836      * Returns the Element this layout is bound to.
48837      * @return {Roo.Element}
48838      */
48839     getEl : function(){
48840         return this.el;
48841     },
48842     
48843     /**
48844      * Returns the specified region.
48845      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48846      * @return {Roo.LayoutRegion}
48847      */
48848     getRegion : function(target){
48849         return this.regions[target.toLowerCase()];
48850     },
48851     
48852     onWindowResize : function(){
48853         if(this.monitorWindowResize){
48854             this.layout();
48855         }
48856     }
48857 });/*
48858  * Based on:
48859  * Ext JS Library 1.1.1
48860  * Copyright(c) 2006-2007, Ext JS, LLC.
48861  *
48862  * Originally Released Under LGPL - original licence link has changed is not relivant.
48863  *
48864  * Fork - LGPL
48865  * <script type="text/javascript">
48866  */
48867 /**
48868  * @class Roo.BorderLayout
48869  * @extends Roo.LayoutManager
48870  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48871  * please see: <br><br>
48872  * <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>
48873  * <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>
48874  * Example:
48875  <pre><code>
48876  var layout = new Roo.BorderLayout(document.body, {
48877     north: {
48878         initialSize: 25,
48879         titlebar: false
48880     },
48881     west: {
48882         split:true,
48883         initialSize: 200,
48884         minSize: 175,
48885         maxSize: 400,
48886         titlebar: true,
48887         collapsible: true
48888     },
48889     east: {
48890         split:true,
48891         initialSize: 202,
48892         minSize: 175,
48893         maxSize: 400,
48894         titlebar: true,
48895         collapsible: true
48896     },
48897     south: {
48898         split:true,
48899         initialSize: 100,
48900         minSize: 100,
48901         maxSize: 200,
48902         titlebar: true,
48903         collapsible: true
48904     },
48905     center: {
48906         titlebar: true,
48907         autoScroll:true,
48908         resizeTabs: true,
48909         minTabWidth: 50,
48910         preferredTabWidth: 150
48911     }
48912 });
48913
48914 // shorthand
48915 var CP = Roo.ContentPanel;
48916
48917 layout.beginUpdate();
48918 layout.add("north", new CP("north", "North"));
48919 layout.add("south", new CP("south", {title: "South", closable: true}));
48920 layout.add("west", new CP("west", {title: "West"}));
48921 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48922 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48923 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48924 layout.getRegion("center").showPanel("center1");
48925 layout.endUpdate();
48926 </code></pre>
48927
48928 <b>The container the layout is rendered into can be either the body element or any other element.
48929 If it is not the body element, the container needs to either be an absolute positioned element,
48930 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48931 the container size if it is not the body element.</b>
48932
48933 * @constructor
48934 * Create a new BorderLayout
48935 * @param {String/HTMLElement/Element} container The container this layout is bound to
48936 * @param {Object} config Configuration options
48937  */
48938 Roo.BorderLayout = function(container, config){
48939     config = config || {};
48940     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48941     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48942     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48943         var target = this.factory.validRegions[i];
48944         if(config[target]){
48945             this.addRegion(target, config[target]);
48946         }
48947     }
48948 };
48949
48950 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48951     /**
48952      * Creates and adds a new region if it doesn't already exist.
48953      * @param {String} target The target region key (north, south, east, west or center).
48954      * @param {Object} config The regions config object
48955      * @return {BorderLayoutRegion} The new region
48956      */
48957     addRegion : function(target, config){
48958         if(!this.regions[target]){
48959             var r = this.factory.create(target, this, config);
48960             this.bindRegion(target, r);
48961         }
48962         return this.regions[target];
48963     },
48964
48965     // private (kinda)
48966     bindRegion : function(name, r){
48967         this.regions[name] = r;
48968         r.on("visibilitychange", this.layout, this);
48969         r.on("paneladded", this.layout, this);
48970         r.on("panelremoved", this.layout, this);
48971         r.on("invalidated", this.layout, this);
48972         r.on("resized", this.onRegionResized, this);
48973         r.on("collapsed", this.onRegionCollapsed, this);
48974         r.on("expanded", this.onRegionExpanded, this);
48975     },
48976
48977     /**
48978      * Performs a layout update.
48979      */
48980     layout : function(){
48981         if(this.updating) return;
48982         var size = this.getViewSize();
48983         var w = size.width;
48984         var h = size.height;
48985         var centerW = w;
48986         var centerH = h;
48987         var centerY = 0;
48988         var centerX = 0;
48989         //var x = 0, y = 0;
48990
48991         var rs = this.regions;
48992         var north = rs["north"];
48993         var south = rs["south"]; 
48994         var west = rs["west"];
48995         var east = rs["east"];
48996         var center = rs["center"];
48997         //if(this.hideOnLayout){ // not supported anymore
48998             //c.el.setStyle("display", "none");
48999         //}
49000         if(north && north.isVisible()){
49001             var b = north.getBox();
49002             var m = north.getMargins();
49003             b.width = w - (m.left+m.right);
49004             b.x = m.left;
49005             b.y = m.top;
49006             centerY = b.height + b.y + m.bottom;
49007             centerH -= centerY;
49008             north.updateBox(this.safeBox(b));
49009         }
49010         if(south && south.isVisible()){
49011             var b = south.getBox();
49012             var m = south.getMargins();
49013             b.width = w - (m.left+m.right);
49014             b.x = m.left;
49015             var totalHeight = (b.height + m.top + m.bottom);
49016             b.y = h - totalHeight + m.top;
49017             centerH -= totalHeight;
49018             south.updateBox(this.safeBox(b));
49019         }
49020         if(west && west.isVisible()){
49021             var b = west.getBox();
49022             var m = west.getMargins();
49023             b.height = centerH - (m.top+m.bottom);
49024             b.x = m.left;
49025             b.y = centerY + m.top;
49026             var totalWidth = (b.width + m.left + m.right);
49027             centerX += totalWidth;
49028             centerW -= totalWidth;
49029             west.updateBox(this.safeBox(b));
49030         }
49031         if(east && east.isVisible()){
49032             var b = east.getBox();
49033             var m = east.getMargins();
49034             b.height = centerH - (m.top+m.bottom);
49035             var totalWidth = (b.width + m.left + m.right);
49036             b.x = w - totalWidth + m.left;
49037             b.y = centerY + m.top;
49038             centerW -= totalWidth;
49039             east.updateBox(this.safeBox(b));
49040         }
49041         if(center){
49042             var m = center.getMargins();
49043             var centerBox = {
49044                 x: centerX + m.left,
49045                 y: centerY + m.top,
49046                 width: centerW - (m.left+m.right),
49047                 height: centerH - (m.top+m.bottom)
49048             };
49049             //if(this.hideOnLayout){
49050                 //center.el.setStyle("display", "block");
49051             //}
49052             center.updateBox(this.safeBox(centerBox));
49053         }
49054         this.el.repaint();
49055         this.fireEvent("layout", this);
49056     },
49057
49058     // private
49059     safeBox : function(box){
49060         box.width = Math.max(0, box.width);
49061         box.height = Math.max(0, box.height);
49062         return box;
49063     },
49064
49065     /**
49066      * Adds a ContentPanel (or subclass) to this layout.
49067      * @param {String} target The target region key (north, south, east, west or center).
49068      * @param {Roo.ContentPanel} panel The panel to add
49069      * @return {Roo.ContentPanel} The added panel
49070      */
49071     add : function(target, panel){
49072          
49073         target = target.toLowerCase();
49074         return this.regions[target].add(panel);
49075     },
49076
49077     /**
49078      * Remove a ContentPanel (or subclass) to this layout.
49079      * @param {String} target The target region key (north, south, east, west or center).
49080      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49081      * @return {Roo.ContentPanel} The removed panel
49082      */
49083     remove : function(target, panel){
49084         target = target.toLowerCase();
49085         return this.regions[target].remove(panel);
49086     },
49087
49088     /**
49089      * Searches all regions for a panel with the specified id
49090      * @param {String} panelId
49091      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49092      */
49093     findPanel : function(panelId){
49094         var rs = this.regions;
49095         for(var target in rs){
49096             if(typeof rs[target] != "function"){
49097                 var p = rs[target].getPanel(panelId);
49098                 if(p){
49099                     return p;
49100                 }
49101             }
49102         }
49103         return null;
49104     },
49105
49106     /**
49107      * Searches all regions for a panel with the specified id and activates (shows) it.
49108      * @param {String/ContentPanel} panelId The panels id or the panel itself
49109      * @return {Roo.ContentPanel} The shown panel or null
49110      */
49111     showPanel : function(panelId) {
49112       var rs = this.regions;
49113       for(var target in rs){
49114          var r = rs[target];
49115          if(typeof r != "function"){
49116             if(r.hasPanel(panelId)){
49117                return r.showPanel(panelId);
49118             }
49119          }
49120       }
49121       return null;
49122    },
49123
49124    /**
49125      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49126      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49127      */
49128     restoreState : function(provider){
49129         if(!provider){
49130             provider = Roo.state.Manager;
49131         }
49132         var sm = new Roo.LayoutStateManager();
49133         sm.init(this, provider);
49134     },
49135
49136     /**
49137      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49138      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49139      * a valid ContentPanel config object.  Example:
49140      * <pre><code>
49141 // Create the main layout
49142 var layout = new Roo.BorderLayout('main-ct', {
49143     west: {
49144         split:true,
49145         minSize: 175,
49146         titlebar: true
49147     },
49148     center: {
49149         title:'Components'
49150     }
49151 }, 'main-ct');
49152
49153 // Create and add multiple ContentPanels at once via configs
49154 layout.batchAdd({
49155    west: {
49156        id: 'source-files',
49157        autoCreate:true,
49158        title:'Ext Source Files',
49159        autoScroll:true,
49160        fitToFrame:true
49161    },
49162    center : {
49163        el: cview,
49164        autoScroll:true,
49165        fitToFrame:true,
49166        toolbar: tb,
49167        resizeEl:'cbody'
49168    }
49169 });
49170 </code></pre>
49171      * @param {Object} regions An object containing ContentPanel configs by region name
49172      */
49173     batchAdd : function(regions){
49174         this.beginUpdate();
49175         for(var rname in regions){
49176             var lr = this.regions[rname];
49177             if(lr){
49178                 this.addTypedPanels(lr, regions[rname]);
49179             }
49180         }
49181         this.endUpdate();
49182     },
49183
49184     // private
49185     addTypedPanels : function(lr, ps){
49186         if(typeof ps == 'string'){
49187             lr.add(new Roo.ContentPanel(ps));
49188         }
49189         else if(ps instanceof Array){
49190             for(var i =0, len = ps.length; i < len; i++){
49191                 this.addTypedPanels(lr, ps[i]);
49192             }
49193         }
49194         else if(!ps.events){ // raw config?
49195             var el = ps.el;
49196             delete ps.el; // prevent conflict
49197             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49198         }
49199         else {  // panel object assumed!
49200             lr.add(ps);
49201         }
49202     },
49203     /**
49204      * Adds a xtype elements to the layout.
49205      * <pre><code>
49206
49207 layout.addxtype({
49208        xtype : 'ContentPanel',
49209        region: 'west',
49210        items: [ .... ]
49211    }
49212 );
49213
49214 layout.addxtype({
49215         xtype : 'NestedLayoutPanel',
49216         region: 'west',
49217         layout: {
49218            center: { },
49219            west: { }   
49220         },
49221         items : [ ... list of content panels or nested layout panels.. ]
49222    }
49223 );
49224 </code></pre>
49225      * @param {Object} cfg Xtype definition of item to add.
49226      */
49227     addxtype : function(cfg)
49228     {
49229         // basically accepts a pannel...
49230         // can accept a layout region..!?!?
49231         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49232         
49233         if (!cfg.xtype.match(/Panel$/)) {
49234             return false;
49235         }
49236         var ret = false;
49237         
49238         if (typeof(cfg.region) == 'undefined') {
49239             Roo.log("Failed to add Panel, region was not set");
49240             Roo.log(cfg);
49241             return false;
49242         }
49243         var region = cfg.region;
49244         delete cfg.region;
49245         
49246           
49247         var xitems = [];
49248         if (cfg.items) {
49249             xitems = cfg.items;
49250             delete cfg.items;
49251         }
49252         var nb = false;
49253         
49254         switch(cfg.xtype) 
49255         {
49256             case 'ContentPanel':  // ContentPanel (el, cfg)
49257             case 'ScrollPanel':  // ContentPanel (el, cfg)
49258             case 'ViewPanel': 
49259                 if(cfg.autoCreate) {
49260                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49261                 } else {
49262                     var el = this.el.createChild();
49263                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49264                 }
49265                 
49266                 this.add(region, ret);
49267                 break;
49268             
49269             
49270             case 'TreePanel': // our new panel!
49271                 cfg.el = this.el.createChild();
49272                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49273                 this.add(region, ret);
49274                 break;
49275             
49276             case 'NestedLayoutPanel': 
49277                 // create a new Layout (which is  a Border Layout...
49278                 var el = this.el.createChild();
49279                 var clayout = cfg.layout;
49280                 delete cfg.layout;
49281                 clayout.items   = clayout.items  || [];
49282                 // replace this exitems with the clayout ones..
49283                 xitems = clayout.items;
49284                  
49285                 
49286                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49287                     cfg.background = false;
49288                 }
49289                 var layout = new Roo.BorderLayout(el, clayout);
49290                 
49291                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49292                 //console.log('adding nested layout panel '  + cfg.toSource());
49293                 this.add(region, ret);
49294                 nb = {}; /// find first...
49295                 break;
49296                 
49297             case 'GridPanel': 
49298             
49299                 // needs grid and region
49300                 
49301                 //var el = this.getRegion(region).el.createChild();
49302                 var el = this.el.createChild();
49303                 // create the grid first...
49304                 
49305                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49306                 delete cfg.grid;
49307                 if (region == 'center' && this.active ) {
49308                     cfg.background = false;
49309                 }
49310                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49311                 
49312                 this.add(region, ret);
49313                 if (cfg.background) {
49314                     ret.on('activate', function(gp) {
49315                         if (!gp.grid.rendered) {
49316                             gp.grid.render();
49317                         }
49318                     });
49319                 } else {
49320                     grid.render();
49321                 }
49322                 break;
49323            
49324            
49325            
49326                 
49327                 
49328                 
49329             default:
49330                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49331                     
49332                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49333                     this.add(region, ret);
49334                 } else {
49335                 
49336                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49337                     return null;
49338                 }
49339                 
49340              // GridPanel (grid, cfg)
49341             
49342         }
49343         this.beginUpdate();
49344         // add children..
49345         var region = '';
49346         var abn = {};
49347         Roo.each(xitems, function(i)  {
49348             region = nb && i.region ? i.region : false;
49349             
49350             var add = ret.addxtype(i);
49351            
49352             if (region) {
49353                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49354                 if (!i.background) {
49355                     abn[region] = nb[region] ;
49356                 }
49357             }
49358             
49359         });
49360         this.endUpdate();
49361
49362         // make the last non-background panel active..
49363         //if (nb) { Roo.log(abn); }
49364         if (nb) {
49365             
49366             for(var r in abn) {
49367                 region = this.getRegion(r);
49368                 if (region) {
49369                     // tried using nb[r], but it does not work..
49370                      
49371                     region.showPanel(abn[r]);
49372                    
49373                 }
49374             }
49375         }
49376         return ret;
49377         
49378     }
49379 });
49380
49381 /**
49382  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49383  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49384  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49385  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49386  * <pre><code>
49387 // shorthand
49388 var CP = Roo.ContentPanel;
49389
49390 var layout = Roo.BorderLayout.create({
49391     north: {
49392         initialSize: 25,
49393         titlebar: false,
49394         panels: [new CP("north", "North")]
49395     },
49396     west: {
49397         split:true,
49398         initialSize: 200,
49399         minSize: 175,
49400         maxSize: 400,
49401         titlebar: true,
49402         collapsible: true,
49403         panels: [new CP("west", {title: "West"})]
49404     },
49405     east: {
49406         split:true,
49407         initialSize: 202,
49408         minSize: 175,
49409         maxSize: 400,
49410         titlebar: true,
49411         collapsible: true,
49412         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49413     },
49414     south: {
49415         split:true,
49416         initialSize: 100,
49417         minSize: 100,
49418         maxSize: 200,
49419         titlebar: true,
49420         collapsible: true,
49421         panels: [new CP("south", {title: "South", closable: true})]
49422     },
49423     center: {
49424         titlebar: true,
49425         autoScroll:true,
49426         resizeTabs: true,
49427         minTabWidth: 50,
49428         preferredTabWidth: 150,
49429         panels: [
49430             new CP("center1", {title: "Close Me", closable: true}),
49431             new CP("center2", {title: "Center Panel", closable: false})
49432         ]
49433     }
49434 }, document.body);
49435
49436 layout.getRegion("center").showPanel("center1");
49437 </code></pre>
49438  * @param config
49439  * @param targetEl
49440  */
49441 Roo.BorderLayout.create = function(config, targetEl){
49442     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49443     layout.beginUpdate();
49444     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49445     for(var j = 0, jlen = regions.length; j < jlen; j++){
49446         var lr = regions[j];
49447         if(layout.regions[lr] && config[lr].panels){
49448             var r = layout.regions[lr];
49449             var ps = config[lr].panels;
49450             layout.addTypedPanels(r, ps);
49451         }
49452     }
49453     layout.endUpdate();
49454     return layout;
49455 };
49456
49457 // private
49458 Roo.BorderLayout.RegionFactory = {
49459     // private
49460     validRegions : ["north","south","east","west","center"],
49461
49462     // private
49463     create : function(target, mgr, config){
49464         target = target.toLowerCase();
49465         if(config.lightweight || config.basic){
49466             return new Roo.BasicLayoutRegion(mgr, config, target);
49467         }
49468         switch(target){
49469             case "north":
49470                 return new Roo.NorthLayoutRegion(mgr, config);
49471             case "south":
49472                 return new Roo.SouthLayoutRegion(mgr, config);
49473             case "east":
49474                 return new Roo.EastLayoutRegion(mgr, config);
49475             case "west":
49476                 return new Roo.WestLayoutRegion(mgr, config);
49477             case "center":
49478                 return new Roo.CenterLayoutRegion(mgr, config);
49479         }
49480         throw 'Layout region "'+target+'" not supported.';
49481     }
49482 };/*
49483  * Based on:
49484  * Ext JS Library 1.1.1
49485  * Copyright(c) 2006-2007, Ext JS, LLC.
49486  *
49487  * Originally Released Under LGPL - original licence link has changed is not relivant.
49488  *
49489  * Fork - LGPL
49490  * <script type="text/javascript">
49491  */
49492  
49493 /**
49494  * @class Roo.BasicLayoutRegion
49495  * @extends Roo.util.Observable
49496  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49497  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49498  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49499  */
49500 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49501     this.mgr = mgr;
49502     this.position  = pos;
49503     this.events = {
49504         /**
49505          * @scope Roo.BasicLayoutRegion
49506          */
49507         
49508         /**
49509          * @event beforeremove
49510          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49511          * @param {Roo.LayoutRegion} this
49512          * @param {Roo.ContentPanel} panel The panel
49513          * @param {Object} e The cancel event object
49514          */
49515         "beforeremove" : true,
49516         /**
49517          * @event invalidated
49518          * Fires when the layout for this region is changed.
49519          * @param {Roo.LayoutRegion} this
49520          */
49521         "invalidated" : true,
49522         /**
49523          * @event visibilitychange
49524          * Fires when this region is shown or hidden 
49525          * @param {Roo.LayoutRegion} this
49526          * @param {Boolean} visibility true or false
49527          */
49528         "visibilitychange" : true,
49529         /**
49530          * @event paneladded
49531          * Fires when a panel is added. 
49532          * @param {Roo.LayoutRegion} this
49533          * @param {Roo.ContentPanel} panel The panel
49534          */
49535         "paneladded" : true,
49536         /**
49537          * @event panelremoved
49538          * Fires when a panel is removed. 
49539          * @param {Roo.LayoutRegion} this
49540          * @param {Roo.ContentPanel} panel The panel
49541          */
49542         "panelremoved" : true,
49543         /**
49544          * @event collapsed
49545          * Fires when this region is collapsed.
49546          * @param {Roo.LayoutRegion} this
49547          */
49548         "collapsed" : true,
49549         /**
49550          * @event expanded
49551          * Fires when this region is expanded.
49552          * @param {Roo.LayoutRegion} this
49553          */
49554         "expanded" : true,
49555         /**
49556          * @event slideshow
49557          * Fires when this region is slid into view.
49558          * @param {Roo.LayoutRegion} this
49559          */
49560         "slideshow" : true,
49561         /**
49562          * @event slidehide
49563          * Fires when this region slides out of view. 
49564          * @param {Roo.LayoutRegion} this
49565          */
49566         "slidehide" : true,
49567         /**
49568          * @event panelactivated
49569          * Fires when a panel is activated. 
49570          * @param {Roo.LayoutRegion} this
49571          * @param {Roo.ContentPanel} panel The activated panel
49572          */
49573         "panelactivated" : true,
49574         /**
49575          * @event resized
49576          * Fires when the user resizes this region. 
49577          * @param {Roo.LayoutRegion} this
49578          * @param {Number} newSize The new size (width for east/west, height for north/south)
49579          */
49580         "resized" : true
49581     };
49582     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49583     this.panels = new Roo.util.MixedCollection();
49584     this.panels.getKey = this.getPanelId.createDelegate(this);
49585     this.box = null;
49586     this.activePanel = null;
49587     // ensure listeners are added...
49588     
49589     if (config.listeners || config.events) {
49590         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49591             listeners : config.listeners || {},
49592             events : config.events || {}
49593         });
49594     }
49595     
49596     if(skipConfig !== true){
49597         this.applyConfig(config);
49598     }
49599 };
49600
49601 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49602     getPanelId : function(p){
49603         return p.getId();
49604     },
49605     
49606     applyConfig : function(config){
49607         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49608         this.config = config;
49609         
49610     },
49611     
49612     /**
49613      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49614      * the width, for horizontal (north, south) the height.
49615      * @param {Number} newSize The new width or height
49616      */
49617     resizeTo : function(newSize){
49618         var el = this.el ? this.el :
49619                  (this.activePanel ? this.activePanel.getEl() : null);
49620         if(el){
49621             switch(this.position){
49622                 case "east":
49623                 case "west":
49624                     el.setWidth(newSize);
49625                     this.fireEvent("resized", this, newSize);
49626                 break;
49627                 case "north":
49628                 case "south":
49629                     el.setHeight(newSize);
49630                     this.fireEvent("resized", this, newSize);
49631                 break;                
49632             }
49633         }
49634     },
49635     
49636     getBox : function(){
49637         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49638     },
49639     
49640     getMargins : function(){
49641         return this.margins;
49642     },
49643     
49644     updateBox : function(box){
49645         this.box = box;
49646         var el = this.activePanel.getEl();
49647         el.dom.style.left = box.x + "px";
49648         el.dom.style.top = box.y + "px";
49649         this.activePanel.setSize(box.width, box.height);
49650     },
49651     
49652     /**
49653      * Returns the container element for this region.
49654      * @return {Roo.Element}
49655      */
49656     getEl : function(){
49657         return this.activePanel;
49658     },
49659     
49660     /**
49661      * Returns true if this region is currently visible.
49662      * @return {Boolean}
49663      */
49664     isVisible : function(){
49665         return this.activePanel ? true : false;
49666     },
49667     
49668     setActivePanel : function(panel){
49669         panel = this.getPanel(panel);
49670         if(this.activePanel && this.activePanel != panel){
49671             this.activePanel.setActiveState(false);
49672             this.activePanel.getEl().setLeftTop(-10000,-10000);
49673         }
49674         this.activePanel = panel;
49675         panel.setActiveState(true);
49676         if(this.box){
49677             panel.setSize(this.box.width, this.box.height);
49678         }
49679         this.fireEvent("panelactivated", this, panel);
49680         this.fireEvent("invalidated");
49681     },
49682     
49683     /**
49684      * Show the specified panel.
49685      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49686      * @return {Roo.ContentPanel} The shown panel or null
49687      */
49688     showPanel : function(panel){
49689         if(panel = this.getPanel(panel)){
49690             this.setActivePanel(panel);
49691         }
49692         return panel;
49693     },
49694     
49695     /**
49696      * Get the active panel for this region.
49697      * @return {Roo.ContentPanel} The active panel or null
49698      */
49699     getActivePanel : function(){
49700         return this.activePanel;
49701     },
49702     
49703     /**
49704      * Add the passed ContentPanel(s)
49705      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49706      * @return {Roo.ContentPanel} The panel added (if only one was added)
49707      */
49708     add : function(panel){
49709         if(arguments.length > 1){
49710             for(var i = 0, len = arguments.length; i < len; i++) {
49711                 this.add(arguments[i]);
49712             }
49713             return null;
49714         }
49715         if(this.hasPanel(panel)){
49716             this.showPanel(panel);
49717             return panel;
49718         }
49719         var el = panel.getEl();
49720         if(el.dom.parentNode != this.mgr.el.dom){
49721             this.mgr.el.dom.appendChild(el.dom);
49722         }
49723         if(panel.setRegion){
49724             panel.setRegion(this);
49725         }
49726         this.panels.add(panel);
49727         el.setStyle("position", "absolute");
49728         if(!panel.background){
49729             this.setActivePanel(panel);
49730             if(this.config.initialSize && this.panels.getCount()==1){
49731                 this.resizeTo(this.config.initialSize);
49732             }
49733         }
49734         this.fireEvent("paneladded", this, panel);
49735         return panel;
49736     },
49737     
49738     /**
49739      * Returns true if the panel is in this region.
49740      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49741      * @return {Boolean}
49742      */
49743     hasPanel : function(panel){
49744         if(typeof panel == "object"){ // must be panel obj
49745             panel = panel.getId();
49746         }
49747         return this.getPanel(panel) ? true : false;
49748     },
49749     
49750     /**
49751      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49752      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49753      * @param {Boolean} preservePanel Overrides the config preservePanel option
49754      * @return {Roo.ContentPanel} The panel that was removed
49755      */
49756     remove : function(panel, preservePanel){
49757         panel = this.getPanel(panel);
49758         if(!panel){
49759             return null;
49760         }
49761         var e = {};
49762         this.fireEvent("beforeremove", this, panel, e);
49763         if(e.cancel === true){
49764             return null;
49765         }
49766         var panelId = panel.getId();
49767         this.panels.removeKey(panelId);
49768         return panel;
49769     },
49770     
49771     /**
49772      * Returns the panel specified or null if it's not in this region.
49773      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49774      * @return {Roo.ContentPanel}
49775      */
49776     getPanel : function(id){
49777         if(typeof id == "object"){ // must be panel obj
49778             return id;
49779         }
49780         return this.panels.get(id);
49781     },
49782     
49783     /**
49784      * Returns this regions position (north/south/east/west/center).
49785      * @return {String} 
49786      */
49787     getPosition: function(){
49788         return this.position;    
49789     }
49790 });/*
49791  * Based on:
49792  * Ext JS Library 1.1.1
49793  * Copyright(c) 2006-2007, Ext JS, LLC.
49794  *
49795  * Originally Released Under LGPL - original licence link has changed is not relivant.
49796  *
49797  * Fork - LGPL
49798  * <script type="text/javascript">
49799  */
49800  
49801 /**
49802  * @class Roo.LayoutRegion
49803  * @extends Roo.BasicLayoutRegion
49804  * This class represents a region in a layout manager.
49805  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49806  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49807  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49808  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49809  * @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})
49810  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49811  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49812  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49813  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49814  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49815  * @cfg {String}    title           The title for the region (overrides panel titles)
49816  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49817  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49818  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49819  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49820  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49821  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49822  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49823  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49824  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49825  * @cfg {Boolean}   showPin         True to show a pin button
49826  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49827  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49828  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49829  * @cfg {Number}    width           For East/West panels
49830  * @cfg {Number}    height          For North/South panels
49831  * @cfg {Boolean}   split           To show the splitter
49832  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49833  */
49834 Roo.LayoutRegion = function(mgr, config, pos){
49835     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49836     var dh = Roo.DomHelper;
49837     /** This region's container element 
49838     * @type Roo.Element */
49839     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49840     /** This region's title element 
49841     * @type Roo.Element */
49842
49843     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49844         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49845         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49846     ]}, true);
49847     this.titleEl.enableDisplayMode();
49848     /** This region's title text element 
49849     * @type HTMLElement */
49850     this.titleTextEl = this.titleEl.dom.firstChild;
49851     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49852     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49853     this.closeBtn.enableDisplayMode();
49854     this.closeBtn.on("click", this.closeClicked, this);
49855     this.closeBtn.hide();
49856
49857     this.createBody(config);
49858     this.visible = true;
49859     this.collapsed = false;
49860
49861     if(config.hideWhenEmpty){
49862         this.hide();
49863         this.on("paneladded", this.validateVisibility, this);
49864         this.on("panelremoved", this.validateVisibility, this);
49865     }
49866     this.applyConfig(config);
49867 };
49868
49869 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49870
49871     createBody : function(){
49872         /** This region's body element 
49873         * @type Roo.Element */
49874         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49875     },
49876
49877     applyConfig : function(c){
49878         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49879             var dh = Roo.DomHelper;
49880             if(c.titlebar !== false){
49881                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49882                 this.collapseBtn.on("click", this.collapse, this);
49883                 this.collapseBtn.enableDisplayMode();
49884
49885                 if(c.showPin === true || this.showPin){
49886                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49887                     this.stickBtn.enableDisplayMode();
49888                     this.stickBtn.on("click", this.expand, this);
49889                     this.stickBtn.hide();
49890                 }
49891             }
49892             /** This region's collapsed element
49893             * @type Roo.Element */
49894             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49895                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49896             ]}, true);
49897             if(c.floatable !== false){
49898                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49899                this.collapsedEl.on("click", this.collapseClick, this);
49900             }
49901
49902             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49903                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49904                    id: "message", unselectable: "on", style:{"float":"left"}});
49905                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49906              }
49907             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49908             this.expandBtn.on("click", this.expand, this);
49909         }
49910         if(this.collapseBtn){
49911             this.collapseBtn.setVisible(c.collapsible == true);
49912         }
49913         this.cmargins = c.cmargins || this.cmargins ||
49914                          (this.position == "west" || this.position == "east" ?
49915                              {top: 0, left: 2, right:2, bottom: 0} :
49916                              {top: 2, left: 0, right:0, bottom: 2});
49917         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49918         this.bottomTabs = c.tabPosition != "top";
49919         this.autoScroll = c.autoScroll || false;
49920         if(this.autoScroll){
49921             this.bodyEl.setStyle("overflow", "auto");
49922         }else{
49923             this.bodyEl.setStyle("overflow", "hidden");
49924         }
49925         //if(c.titlebar !== false){
49926             if((!c.titlebar && !c.title) || c.titlebar === false){
49927                 this.titleEl.hide();
49928             }else{
49929                 this.titleEl.show();
49930                 if(c.title){
49931                     this.titleTextEl.innerHTML = c.title;
49932                 }
49933             }
49934         //}
49935         this.duration = c.duration || .30;
49936         this.slideDuration = c.slideDuration || .45;
49937         this.config = c;
49938         if(c.collapsed){
49939             this.collapse(true);
49940         }
49941         if(c.hidden){
49942             this.hide();
49943         }
49944     },
49945     /**
49946      * Returns true if this region is currently visible.
49947      * @return {Boolean}
49948      */
49949     isVisible : function(){
49950         return this.visible;
49951     },
49952
49953     /**
49954      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49955      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49956      */
49957     setCollapsedTitle : function(title){
49958         title = title || "&#160;";
49959         if(this.collapsedTitleTextEl){
49960             this.collapsedTitleTextEl.innerHTML = title;
49961         }
49962     },
49963
49964     getBox : function(){
49965         var b;
49966         if(!this.collapsed){
49967             b = this.el.getBox(false, true);
49968         }else{
49969             b = this.collapsedEl.getBox(false, true);
49970         }
49971         return b;
49972     },
49973
49974     getMargins : function(){
49975         return this.collapsed ? this.cmargins : this.margins;
49976     },
49977
49978     highlight : function(){
49979         this.el.addClass("x-layout-panel-dragover");
49980     },
49981
49982     unhighlight : function(){
49983         this.el.removeClass("x-layout-panel-dragover");
49984     },
49985
49986     updateBox : function(box){
49987         this.box = box;
49988         if(!this.collapsed){
49989             this.el.dom.style.left = box.x + "px";
49990             this.el.dom.style.top = box.y + "px";
49991             this.updateBody(box.width, box.height);
49992         }else{
49993             this.collapsedEl.dom.style.left = box.x + "px";
49994             this.collapsedEl.dom.style.top = box.y + "px";
49995             this.collapsedEl.setSize(box.width, box.height);
49996         }
49997         if(this.tabs){
49998             this.tabs.autoSizeTabs();
49999         }
50000     },
50001
50002     updateBody : function(w, h){
50003         if(w !== null){
50004             this.el.setWidth(w);
50005             w -= this.el.getBorderWidth("rl");
50006             if(this.config.adjustments){
50007                 w += this.config.adjustments[0];
50008             }
50009         }
50010         if(h !== null){
50011             this.el.setHeight(h);
50012             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50013             h -= this.el.getBorderWidth("tb");
50014             if(this.config.adjustments){
50015                 h += this.config.adjustments[1];
50016             }
50017             this.bodyEl.setHeight(h);
50018             if(this.tabs){
50019                 h = this.tabs.syncHeight(h);
50020             }
50021         }
50022         if(this.panelSize){
50023             w = w !== null ? w : this.panelSize.width;
50024             h = h !== null ? h : this.panelSize.height;
50025         }
50026         if(this.activePanel){
50027             var el = this.activePanel.getEl();
50028             w = w !== null ? w : el.getWidth();
50029             h = h !== null ? h : el.getHeight();
50030             this.panelSize = {width: w, height: h};
50031             this.activePanel.setSize(w, h);
50032         }
50033         if(Roo.isIE && this.tabs){
50034             this.tabs.el.repaint();
50035         }
50036     },
50037
50038     /**
50039      * Returns the container element for this region.
50040      * @return {Roo.Element}
50041      */
50042     getEl : function(){
50043         return this.el;
50044     },
50045
50046     /**
50047      * Hides this region.
50048      */
50049     hide : function(){
50050         if(!this.collapsed){
50051             this.el.dom.style.left = "-2000px";
50052             this.el.hide();
50053         }else{
50054             this.collapsedEl.dom.style.left = "-2000px";
50055             this.collapsedEl.hide();
50056         }
50057         this.visible = false;
50058         this.fireEvent("visibilitychange", this, false);
50059     },
50060
50061     /**
50062      * Shows this region if it was previously hidden.
50063      */
50064     show : function(){
50065         if(!this.collapsed){
50066             this.el.show();
50067         }else{
50068             this.collapsedEl.show();
50069         }
50070         this.visible = true;
50071         this.fireEvent("visibilitychange", this, true);
50072     },
50073
50074     closeClicked : function(){
50075         if(this.activePanel){
50076             this.remove(this.activePanel);
50077         }
50078     },
50079
50080     collapseClick : function(e){
50081         if(this.isSlid){
50082            e.stopPropagation();
50083            this.slideIn();
50084         }else{
50085            e.stopPropagation();
50086            this.slideOut();
50087         }
50088     },
50089
50090     /**
50091      * Collapses this region.
50092      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50093      */
50094     collapse : function(skipAnim){
50095         if(this.collapsed) return;
50096         this.collapsed = true;
50097         if(this.split){
50098             this.split.el.hide();
50099         }
50100         if(this.config.animate && skipAnim !== true){
50101             this.fireEvent("invalidated", this);
50102             this.animateCollapse();
50103         }else{
50104             this.el.setLocation(-20000,-20000);
50105             this.el.hide();
50106             this.collapsedEl.show();
50107             this.fireEvent("collapsed", this);
50108             this.fireEvent("invalidated", this);
50109         }
50110     },
50111
50112     animateCollapse : function(){
50113         // overridden
50114     },
50115
50116     /**
50117      * Expands this region if it was previously collapsed.
50118      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50119      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50120      */
50121     expand : function(e, skipAnim){
50122         if(e) e.stopPropagation();
50123         if(!this.collapsed || this.el.hasActiveFx()) return;
50124         if(this.isSlid){
50125             this.afterSlideIn();
50126             skipAnim = true;
50127         }
50128         this.collapsed = false;
50129         if(this.config.animate && skipAnim !== true){
50130             this.animateExpand();
50131         }else{
50132             this.el.show();
50133             if(this.split){
50134                 this.split.el.show();
50135             }
50136             this.collapsedEl.setLocation(-2000,-2000);
50137             this.collapsedEl.hide();
50138             this.fireEvent("invalidated", this);
50139             this.fireEvent("expanded", this);
50140         }
50141     },
50142
50143     animateExpand : function(){
50144         // overridden
50145     },
50146
50147     initTabs : function()
50148     {
50149         this.bodyEl.setStyle("overflow", "hidden");
50150         var ts = new Roo.TabPanel(
50151                 this.bodyEl.dom,
50152                 {
50153                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50154                     disableTooltips: this.config.disableTabTips,
50155                     toolbar : this.config.toolbar
50156                 }
50157         );
50158         if(this.config.hideTabs){
50159             ts.stripWrap.setDisplayed(false);
50160         }
50161         this.tabs = ts;
50162         ts.resizeTabs = this.config.resizeTabs === true;
50163         ts.minTabWidth = this.config.minTabWidth || 40;
50164         ts.maxTabWidth = this.config.maxTabWidth || 250;
50165         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50166         ts.monitorResize = false;
50167         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50168         ts.bodyEl.addClass('x-layout-tabs-body');
50169         this.panels.each(this.initPanelAsTab, this);
50170     },
50171
50172     initPanelAsTab : function(panel){
50173         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50174                     this.config.closeOnTab && panel.isClosable());
50175         if(panel.tabTip !== undefined){
50176             ti.setTooltip(panel.tabTip);
50177         }
50178         ti.on("activate", function(){
50179               this.setActivePanel(panel);
50180         }, this);
50181         if(this.config.closeOnTab){
50182             ti.on("beforeclose", function(t, e){
50183                 e.cancel = true;
50184                 this.remove(panel);
50185             }, this);
50186         }
50187         return ti;
50188     },
50189
50190     updatePanelTitle : function(panel, title){
50191         if(this.activePanel == panel){
50192             this.updateTitle(title);
50193         }
50194         if(this.tabs){
50195             var ti = this.tabs.getTab(panel.getEl().id);
50196             ti.setText(title);
50197             if(panel.tabTip !== undefined){
50198                 ti.setTooltip(panel.tabTip);
50199             }
50200         }
50201     },
50202
50203     updateTitle : function(title){
50204         if(this.titleTextEl && !this.config.title){
50205             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50206         }
50207     },
50208
50209     setActivePanel : function(panel){
50210         panel = this.getPanel(panel);
50211         if(this.activePanel && this.activePanel != panel){
50212             this.activePanel.setActiveState(false);
50213         }
50214         this.activePanel = panel;
50215         panel.setActiveState(true);
50216         if(this.panelSize){
50217             panel.setSize(this.panelSize.width, this.panelSize.height);
50218         }
50219         if(this.closeBtn){
50220             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50221         }
50222         this.updateTitle(panel.getTitle());
50223         if(this.tabs){
50224             this.fireEvent("invalidated", this);
50225         }
50226         this.fireEvent("panelactivated", this, panel);
50227     },
50228
50229     /**
50230      * Shows the specified panel.
50231      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50232      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50233      */
50234     showPanel : function(panel){
50235         if(panel = this.getPanel(panel)){
50236             if(this.tabs){
50237                 var tab = this.tabs.getTab(panel.getEl().id);
50238                 if(tab.isHidden()){
50239                     this.tabs.unhideTab(tab.id);
50240                 }
50241                 tab.activate();
50242             }else{
50243                 this.setActivePanel(panel);
50244             }
50245         }
50246         return panel;
50247     },
50248
50249     /**
50250      * Get the active panel for this region.
50251      * @return {Roo.ContentPanel} The active panel or null
50252      */
50253     getActivePanel : function(){
50254         return this.activePanel;
50255     },
50256
50257     validateVisibility : function(){
50258         if(this.panels.getCount() < 1){
50259             this.updateTitle("&#160;");
50260             this.closeBtn.hide();
50261             this.hide();
50262         }else{
50263             if(!this.isVisible()){
50264                 this.show();
50265             }
50266         }
50267     },
50268
50269     /**
50270      * Adds the passed ContentPanel(s) to this region.
50271      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50272      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50273      */
50274     add : function(panel){
50275         if(arguments.length > 1){
50276             for(var i = 0, len = arguments.length; i < len; i++) {
50277                 this.add(arguments[i]);
50278             }
50279             return null;
50280         }
50281         if(this.hasPanel(panel)){
50282             this.showPanel(panel);
50283             return panel;
50284         }
50285         panel.setRegion(this);
50286         this.panels.add(panel);
50287         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50288             this.bodyEl.dom.appendChild(panel.getEl().dom);
50289             if(panel.background !== true){
50290                 this.setActivePanel(panel);
50291             }
50292             this.fireEvent("paneladded", this, panel);
50293             return panel;
50294         }
50295         if(!this.tabs){
50296             this.initTabs();
50297         }else{
50298             this.initPanelAsTab(panel);
50299         }
50300         if(panel.background !== true){
50301             this.tabs.activate(panel.getEl().id);
50302         }
50303         this.fireEvent("paneladded", this, panel);
50304         return panel;
50305     },
50306
50307     /**
50308      * Hides the tab for the specified panel.
50309      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50310      */
50311     hidePanel : function(panel){
50312         if(this.tabs && (panel = this.getPanel(panel))){
50313             this.tabs.hideTab(panel.getEl().id);
50314         }
50315     },
50316
50317     /**
50318      * Unhides the tab for a previously hidden panel.
50319      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50320      */
50321     unhidePanel : function(panel){
50322         if(this.tabs && (panel = this.getPanel(panel))){
50323             this.tabs.unhideTab(panel.getEl().id);
50324         }
50325     },
50326
50327     clearPanels : function(){
50328         while(this.panels.getCount() > 0){
50329              this.remove(this.panels.first());
50330         }
50331     },
50332
50333     /**
50334      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50335      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50336      * @param {Boolean} preservePanel Overrides the config preservePanel option
50337      * @return {Roo.ContentPanel} The panel that was removed
50338      */
50339     remove : function(panel, preservePanel){
50340         panel = this.getPanel(panel);
50341         if(!panel){
50342             return null;
50343         }
50344         var e = {};
50345         this.fireEvent("beforeremove", this, panel, e);
50346         if(e.cancel === true){
50347             return null;
50348         }
50349         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50350         var panelId = panel.getId();
50351         this.panels.removeKey(panelId);
50352         if(preservePanel){
50353             document.body.appendChild(panel.getEl().dom);
50354         }
50355         if(this.tabs){
50356             this.tabs.removeTab(panel.getEl().id);
50357         }else if (!preservePanel){
50358             this.bodyEl.dom.removeChild(panel.getEl().dom);
50359         }
50360         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50361             var p = this.panels.first();
50362             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50363             tempEl.appendChild(p.getEl().dom);
50364             this.bodyEl.update("");
50365             this.bodyEl.dom.appendChild(p.getEl().dom);
50366             tempEl = null;
50367             this.updateTitle(p.getTitle());
50368             this.tabs = null;
50369             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50370             this.setActivePanel(p);
50371         }
50372         panel.setRegion(null);
50373         if(this.activePanel == panel){
50374             this.activePanel = null;
50375         }
50376         if(this.config.autoDestroy !== false && preservePanel !== true){
50377             try{panel.destroy();}catch(e){}
50378         }
50379         this.fireEvent("panelremoved", this, panel);
50380         return panel;
50381     },
50382
50383     /**
50384      * Returns the TabPanel component used by this region
50385      * @return {Roo.TabPanel}
50386      */
50387     getTabs : function(){
50388         return this.tabs;
50389     },
50390
50391     createTool : function(parentEl, className){
50392         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50393             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50394         btn.addClassOnOver("x-layout-tools-button-over");
50395         return btn;
50396     }
50397 });/*
50398  * Based on:
50399  * Ext JS Library 1.1.1
50400  * Copyright(c) 2006-2007, Ext JS, LLC.
50401  *
50402  * Originally Released Under LGPL - original licence link has changed is not relivant.
50403  *
50404  * Fork - LGPL
50405  * <script type="text/javascript">
50406  */
50407  
50408
50409
50410 /**
50411  * @class Roo.SplitLayoutRegion
50412  * @extends Roo.LayoutRegion
50413  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50414  */
50415 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50416     this.cursor = cursor;
50417     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50418 };
50419
50420 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50421     splitTip : "Drag to resize.",
50422     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50423     useSplitTips : false,
50424
50425     applyConfig : function(config){
50426         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50427         if(config.split){
50428             if(!this.split){
50429                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50430                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50431                 /** The SplitBar for this region 
50432                 * @type Roo.SplitBar */
50433                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50434                 this.split.on("moved", this.onSplitMove, this);
50435                 this.split.useShim = config.useShim === true;
50436                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50437                 if(this.useSplitTips){
50438                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50439                 }
50440                 if(config.collapsible){
50441                     this.split.el.on("dblclick", this.collapse,  this);
50442                 }
50443             }
50444             if(typeof config.minSize != "undefined"){
50445                 this.split.minSize = config.minSize;
50446             }
50447             if(typeof config.maxSize != "undefined"){
50448                 this.split.maxSize = config.maxSize;
50449             }
50450             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50451                 this.hideSplitter();
50452             }
50453         }
50454     },
50455
50456     getHMaxSize : function(){
50457          var cmax = this.config.maxSize || 10000;
50458          var center = this.mgr.getRegion("center");
50459          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50460     },
50461
50462     getVMaxSize : function(){
50463          var cmax = this.config.maxSize || 10000;
50464          var center = this.mgr.getRegion("center");
50465          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50466     },
50467
50468     onSplitMove : function(split, newSize){
50469         this.fireEvent("resized", this, newSize);
50470     },
50471     
50472     /** 
50473      * Returns the {@link Roo.SplitBar} for this region.
50474      * @return {Roo.SplitBar}
50475      */
50476     getSplitBar : function(){
50477         return this.split;
50478     },
50479     
50480     hide : function(){
50481         this.hideSplitter();
50482         Roo.SplitLayoutRegion.superclass.hide.call(this);
50483     },
50484
50485     hideSplitter : function(){
50486         if(this.split){
50487             this.split.el.setLocation(-2000,-2000);
50488             this.split.el.hide();
50489         }
50490     },
50491
50492     show : function(){
50493         if(this.split){
50494             this.split.el.show();
50495         }
50496         Roo.SplitLayoutRegion.superclass.show.call(this);
50497     },
50498     
50499     beforeSlide: function(){
50500         if(Roo.isGecko){// firefox overflow auto bug workaround
50501             this.bodyEl.clip();
50502             if(this.tabs) this.tabs.bodyEl.clip();
50503             if(this.activePanel){
50504                 this.activePanel.getEl().clip();
50505                 
50506                 if(this.activePanel.beforeSlide){
50507                     this.activePanel.beforeSlide();
50508                 }
50509             }
50510         }
50511     },
50512     
50513     afterSlide : function(){
50514         if(Roo.isGecko){// firefox overflow auto bug workaround
50515             this.bodyEl.unclip();
50516             if(this.tabs) this.tabs.bodyEl.unclip();
50517             if(this.activePanel){
50518                 this.activePanel.getEl().unclip();
50519                 if(this.activePanel.afterSlide){
50520                     this.activePanel.afterSlide();
50521                 }
50522             }
50523         }
50524     },
50525
50526     initAutoHide : function(){
50527         if(this.autoHide !== false){
50528             if(!this.autoHideHd){
50529                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50530                 this.autoHideHd = {
50531                     "mouseout": function(e){
50532                         if(!e.within(this.el, true)){
50533                             st.delay(500);
50534                         }
50535                     },
50536                     "mouseover" : function(e){
50537                         st.cancel();
50538                     },
50539                     scope : this
50540                 };
50541             }
50542             this.el.on(this.autoHideHd);
50543         }
50544     },
50545
50546     clearAutoHide : function(){
50547         if(this.autoHide !== false){
50548             this.el.un("mouseout", this.autoHideHd.mouseout);
50549             this.el.un("mouseover", this.autoHideHd.mouseover);
50550         }
50551     },
50552
50553     clearMonitor : function(){
50554         Roo.get(document).un("click", this.slideInIf, this);
50555     },
50556
50557     // these names are backwards but not changed for compat
50558     slideOut : function(){
50559         if(this.isSlid || this.el.hasActiveFx()){
50560             return;
50561         }
50562         this.isSlid = true;
50563         if(this.collapseBtn){
50564             this.collapseBtn.hide();
50565         }
50566         this.closeBtnState = this.closeBtn.getStyle('display');
50567         this.closeBtn.hide();
50568         if(this.stickBtn){
50569             this.stickBtn.show();
50570         }
50571         this.el.show();
50572         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50573         this.beforeSlide();
50574         this.el.setStyle("z-index", 10001);
50575         this.el.slideIn(this.getSlideAnchor(), {
50576             callback: function(){
50577                 this.afterSlide();
50578                 this.initAutoHide();
50579                 Roo.get(document).on("click", this.slideInIf, this);
50580                 this.fireEvent("slideshow", this);
50581             },
50582             scope: this,
50583             block: true
50584         });
50585     },
50586
50587     afterSlideIn : function(){
50588         this.clearAutoHide();
50589         this.isSlid = false;
50590         this.clearMonitor();
50591         this.el.setStyle("z-index", "");
50592         if(this.collapseBtn){
50593             this.collapseBtn.show();
50594         }
50595         this.closeBtn.setStyle('display', this.closeBtnState);
50596         if(this.stickBtn){
50597             this.stickBtn.hide();
50598         }
50599         this.fireEvent("slidehide", this);
50600     },
50601
50602     slideIn : function(cb){
50603         if(!this.isSlid || this.el.hasActiveFx()){
50604             Roo.callback(cb);
50605             return;
50606         }
50607         this.isSlid = false;
50608         this.beforeSlide();
50609         this.el.slideOut(this.getSlideAnchor(), {
50610             callback: function(){
50611                 this.el.setLeftTop(-10000, -10000);
50612                 this.afterSlide();
50613                 this.afterSlideIn();
50614                 Roo.callback(cb);
50615             },
50616             scope: this,
50617             block: true
50618         });
50619     },
50620     
50621     slideInIf : function(e){
50622         if(!e.within(this.el)){
50623             this.slideIn();
50624         }
50625     },
50626
50627     animateCollapse : function(){
50628         this.beforeSlide();
50629         this.el.setStyle("z-index", 20000);
50630         var anchor = this.getSlideAnchor();
50631         this.el.slideOut(anchor, {
50632             callback : function(){
50633                 this.el.setStyle("z-index", "");
50634                 this.collapsedEl.slideIn(anchor, {duration:.3});
50635                 this.afterSlide();
50636                 this.el.setLocation(-10000,-10000);
50637                 this.el.hide();
50638                 this.fireEvent("collapsed", this);
50639             },
50640             scope: this,
50641             block: true
50642         });
50643     },
50644
50645     animateExpand : function(){
50646         this.beforeSlide();
50647         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50648         this.el.setStyle("z-index", 20000);
50649         this.collapsedEl.hide({
50650             duration:.1
50651         });
50652         this.el.slideIn(this.getSlideAnchor(), {
50653             callback : function(){
50654                 this.el.setStyle("z-index", "");
50655                 this.afterSlide();
50656                 if(this.split){
50657                     this.split.el.show();
50658                 }
50659                 this.fireEvent("invalidated", this);
50660                 this.fireEvent("expanded", this);
50661             },
50662             scope: this,
50663             block: true
50664         });
50665     },
50666
50667     anchors : {
50668         "west" : "left",
50669         "east" : "right",
50670         "north" : "top",
50671         "south" : "bottom"
50672     },
50673
50674     sanchors : {
50675         "west" : "l",
50676         "east" : "r",
50677         "north" : "t",
50678         "south" : "b"
50679     },
50680
50681     canchors : {
50682         "west" : "tl-tr",
50683         "east" : "tr-tl",
50684         "north" : "tl-bl",
50685         "south" : "bl-tl"
50686     },
50687
50688     getAnchor : function(){
50689         return this.anchors[this.position];
50690     },
50691
50692     getCollapseAnchor : function(){
50693         return this.canchors[this.position];
50694     },
50695
50696     getSlideAnchor : function(){
50697         return this.sanchors[this.position];
50698     },
50699
50700     getAlignAdj : function(){
50701         var cm = this.cmargins;
50702         switch(this.position){
50703             case "west":
50704                 return [0, 0];
50705             break;
50706             case "east":
50707                 return [0, 0];
50708             break;
50709             case "north":
50710                 return [0, 0];
50711             break;
50712             case "south":
50713                 return [0, 0];
50714             break;
50715         }
50716     },
50717
50718     getExpandAdj : function(){
50719         var c = this.collapsedEl, cm = this.cmargins;
50720         switch(this.position){
50721             case "west":
50722                 return [-(cm.right+c.getWidth()+cm.left), 0];
50723             break;
50724             case "east":
50725                 return [cm.right+c.getWidth()+cm.left, 0];
50726             break;
50727             case "north":
50728                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50729             break;
50730             case "south":
50731                 return [0, cm.top+cm.bottom+c.getHeight()];
50732             break;
50733         }
50734     }
50735 });/*
50736  * Based on:
50737  * Ext JS Library 1.1.1
50738  * Copyright(c) 2006-2007, Ext JS, LLC.
50739  *
50740  * Originally Released Under LGPL - original licence link has changed is not relivant.
50741  *
50742  * Fork - LGPL
50743  * <script type="text/javascript">
50744  */
50745 /*
50746  * These classes are private internal classes
50747  */
50748 Roo.CenterLayoutRegion = function(mgr, config){
50749     Roo.LayoutRegion.call(this, mgr, config, "center");
50750     this.visible = true;
50751     this.minWidth = config.minWidth || 20;
50752     this.minHeight = config.minHeight || 20;
50753 };
50754
50755 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50756     hide : function(){
50757         // center panel can't be hidden
50758     },
50759     
50760     show : function(){
50761         // center panel can't be hidden
50762     },
50763     
50764     getMinWidth: function(){
50765         return this.minWidth;
50766     },
50767     
50768     getMinHeight: function(){
50769         return this.minHeight;
50770     }
50771 });
50772
50773
50774 Roo.NorthLayoutRegion = function(mgr, config){
50775     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50776     if(this.split){
50777         this.split.placement = Roo.SplitBar.TOP;
50778         this.split.orientation = Roo.SplitBar.VERTICAL;
50779         this.split.el.addClass("x-layout-split-v");
50780     }
50781     var size = config.initialSize || config.height;
50782     if(typeof size != "undefined"){
50783         this.el.setHeight(size);
50784     }
50785 };
50786 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50787     orientation: Roo.SplitBar.VERTICAL,
50788     getBox : function(){
50789         if(this.collapsed){
50790             return this.collapsedEl.getBox();
50791         }
50792         var box = this.el.getBox();
50793         if(this.split){
50794             box.height += this.split.el.getHeight();
50795         }
50796         return box;
50797     },
50798     
50799     updateBox : function(box){
50800         if(this.split && !this.collapsed){
50801             box.height -= this.split.el.getHeight();
50802             this.split.el.setLeft(box.x);
50803             this.split.el.setTop(box.y+box.height);
50804             this.split.el.setWidth(box.width);
50805         }
50806         if(this.collapsed){
50807             this.updateBody(box.width, null);
50808         }
50809         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50810     }
50811 });
50812
50813 Roo.SouthLayoutRegion = function(mgr, config){
50814     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50815     if(this.split){
50816         this.split.placement = Roo.SplitBar.BOTTOM;
50817         this.split.orientation = Roo.SplitBar.VERTICAL;
50818         this.split.el.addClass("x-layout-split-v");
50819     }
50820     var size = config.initialSize || config.height;
50821     if(typeof size != "undefined"){
50822         this.el.setHeight(size);
50823     }
50824 };
50825 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50826     orientation: Roo.SplitBar.VERTICAL,
50827     getBox : function(){
50828         if(this.collapsed){
50829             return this.collapsedEl.getBox();
50830         }
50831         var box = this.el.getBox();
50832         if(this.split){
50833             var sh = this.split.el.getHeight();
50834             box.height += sh;
50835             box.y -= sh;
50836         }
50837         return box;
50838     },
50839     
50840     updateBox : function(box){
50841         if(this.split && !this.collapsed){
50842             var sh = this.split.el.getHeight();
50843             box.height -= sh;
50844             box.y += sh;
50845             this.split.el.setLeft(box.x);
50846             this.split.el.setTop(box.y-sh);
50847             this.split.el.setWidth(box.width);
50848         }
50849         if(this.collapsed){
50850             this.updateBody(box.width, null);
50851         }
50852         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50853     }
50854 });
50855
50856 Roo.EastLayoutRegion = function(mgr, config){
50857     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50858     if(this.split){
50859         this.split.placement = Roo.SplitBar.RIGHT;
50860         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50861         this.split.el.addClass("x-layout-split-h");
50862     }
50863     var size = config.initialSize || config.width;
50864     if(typeof size != "undefined"){
50865         this.el.setWidth(size);
50866     }
50867 };
50868 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50869     orientation: Roo.SplitBar.HORIZONTAL,
50870     getBox : function(){
50871         if(this.collapsed){
50872             return this.collapsedEl.getBox();
50873         }
50874         var box = this.el.getBox();
50875         if(this.split){
50876             var sw = this.split.el.getWidth();
50877             box.width += sw;
50878             box.x -= sw;
50879         }
50880         return box;
50881     },
50882
50883     updateBox : function(box){
50884         if(this.split && !this.collapsed){
50885             var sw = this.split.el.getWidth();
50886             box.width -= sw;
50887             this.split.el.setLeft(box.x);
50888             this.split.el.setTop(box.y);
50889             this.split.el.setHeight(box.height);
50890             box.x += sw;
50891         }
50892         if(this.collapsed){
50893             this.updateBody(null, box.height);
50894         }
50895         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50896     }
50897 });
50898
50899 Roo.WestLayoutRegion = function(mgr, config){
50900     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50901     if(this.split){
50902         this.split.placement = Roo.SplitBar.LEFT;
50903         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50904         this.split.el.addClass("x-layout-split-h");
50905     }
50906     var size = config.initialSize || config.width;
50907     if(typeof size != "undefined"){
50908         this.el.setWidth(size);
50909     }
50910 };
50911 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50912     orientation: Roo.SplitBar.HORIZONTAL,
50913     getBox : function(){
50914         if(this.collapsed){
50915             return this.collapsedEl.getBox();
50916         }
50917         var box = this.el.getBox();
50918         if(this.split){
50919             box.width += this.split.el.getWidth();
50920         }
50921         return box;
50922     },
50923     
50924     updateBox : function(box){
50925         if(this.split && !this.collapsed){
50926             var sw = this.split.el.getWidth();
50927             box.width -= sw;
50928             this.split.el.setLeft(box.x+box.width);
50929             this.split.el.setTop(box.y);
50930             this.split.el.setHeight(box.height);
50931         }
50932         if(this.collapsed){
50933             this.updateBody(null, box.height);
50934         }
50935         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50936     }
50937 });
50938 /*
50939  * Based on:
50940  * Ext JS Library 1.1.1
50941  * Copyright(c) 2006-2007, Ext JS, LLC.
50942  *
50943  * Originally Released Under LGPL - original licence link has changed is not relivant.
50944  *
50945  * Fork - LGPL
50946  * <script type="text/javascript">
50947  */
50948  
50949  
50950 /*
50951  * Private internal class for reading and applying state
50952  */
50953 Roo.LayoutStateManager = function(layout){
50954      // default empty state
50955      this.state = {
50956         north: {},
50957         south: {},
50958         east: {},
50959         west: {}       
50960     };
50961 };
50962
50963 Roo.LayoutStateManager.prototype = {
50964     init : function(layout, provider){
50965         this.provider = provider;
50966         var state = provider.get(layout.id+"-layout-state");
50967         if(state){
50968             var wasUpdating = layout.isUpdating();
50969             if(!wasUpdating){
50970                 layout.beginUpdate();
50971             }
50972             for(var key in state){
50973                 if(typeof state[key] != "function"){
50974                     var rstate = state[key];
50975                     var r = layout.getRegion(key);
50976                     if(r && rstate){
50977                         if(rstate.size){
50978                             r.resizeTo(rstate.size);
50979                         }
50980                         if(rstate.collapsed == true){
50981                             r.collapse(true);
50982                         }else{
50983                             r.expand(null, true);
50984                         }
50985                     }
50986                 }
50987             }
50988             if(!wasUpdating){
50989                 layout.endUpdate();
50990             }
50991             this.state = state; 
50992         }
50993         this.layout = layout;
50994         layout.on("regionresized", this.onRegionResized, this);
50995         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50996         layout.on("regionexpanded", this.onRegionExpanded, this);
50997     },
50998     
50999     storeState : function(){
51000         this.provider.set(this.layout.id+"-layout-state", this.state);
51001     },
51002     
51003     onRegionResized : function(region, newSize){
51004         this.state[region.getPosition()].size = newSize;
51005         this.storeState();
51006     },
51007     
51008     onRegionCollapsed : function(region){
51009         this.state[region.getPosition()].collapsed = true;
51010         this.storeState();
51011     },
51012     
51013     onRegionExpanded : function(region){
51014         this.state[region.getPosition()].collapsed = false;
51015         this.storeState();
51016     }
51017 };/*
51018  * Based on:
51019  * Ext JS Library 1.1.1
51020  * Copyright(c) 2006-2007, Ext JS, LLC.
51021  *
51022  * Originally Released Under LGPL - original licence link has changed is not relivant.
51023  *
51024  * Fork - LGPL
51025  * <script type="text/javascript">
51026  */
51027 /**
51028  * @class Roo.ContentPanel
51029  * @extends Roo.util.Observable
51030  * A basic ContentPanel element.
51031  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51032  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51033  * @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
51034  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51035  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51036  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51037  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51038  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51039  * @cfg {String} title          The title for this panel
51040  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51041  * @cfg {String} url            Calls {@link #setUrl} with this value
51042  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51043  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51044  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51045  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51046
51047  * @constructor
51048  * Create a new ContentPanel.
51049  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51050  * @param {String/Object} config A string to set only the title or a config object
51051  * @param {String} content (optional) Set the HTML content for this panel
51052  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51053  */
51054 Roo.ContentPanel = function(el, config, content){
51055     
51056      
51057     /*
51058     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51059         config = el;
51060         el = Roo.id();
51061     }
51062     if (config && config.parentLayout) { 
51063         el = config.parentLayout.el.createChild(); 
51064     }
51065     */
51066     if(el.autoCreate){ // xtype is available if this is called from factory
51067         config = el;
51068         el = Roo.id();
51069     }
51070     this.el = Roo.get(el);
51071     if(!this.el && config && config.autoCreate){
51072         if(typeof config.autoCreate == "object"){
51073             if(!config.autoCreate.id){
51074                 config.autoCreate.id = config.id||el;
51075             }
51076             this.el = Roo.DomHelper.append(document.body,
51077                         config.autoCreate, true);
51078         }else{
51079             this.el = Roo.DomHelper.append(document.body,
51080                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51081         }
51082     }
51083     this.closable = false;
51084     this.loaded = false;
51085     this.active = false;
51086     if(typeof config == "string"){
51087         this.title = config;
51088     }else{
51089         Roo.apply(this, config);
51090     }
51091     
51092     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51093         this.wrapEl = this.el.wrap();
51094         this.toolbar.container = this.el.insertSibling(false, 'before');
51095         this.toolbar = new Roo.Toolbar(this.toolbar);
51096     }
51097     
51098     // xtype created footer. - not sure if will work as we normally have to render first..
51099     if (this.footer && !this.footer.el && this.footer.xtype) {
51100         if (!this.wrapEl) {
51101             this.wrapEl = this.el.wrap();
51102         }
51103     
51104         this.footer.container = this.wrapEl.createChild();
51105          
51106         this.footer = Roo.factory(this.footer, Roo);
51107         
51108     }
51109     
51110     if(this.resizeEl){
51111         this.resizeEl = Roo.get(this.resizeEl, true);
51112     }else{
51113         this.resizeEl = this.el;
51114     }
51115     // handle view.xtype
51116     
51117  
51118     
51119     
51120     this.addEvents({
51121         /**
51122          * @event activate
51123          * Fires when this panel is activated. 
51124          * @param {Roo.ContentPanel} this
51125          */
51126         "activate" : true,
51127         /**
51128          * @event deactivate
51129          * Fires when this panel is activated. 
51130          * @param {Roo.ContentPanel} this
51131          */
51132         "deactivate" : true,
51133
51134         /**
51135          * @event resize
51136          * Fires when this panel is resized if fitToFrame is true.
51137          * @param {Roo.ContentPanel} this
51138          * @param {Number} width The width after any component adjustments
51139          * @param {Number} height The height after any component adjustments
51140          */
51141         "resize" : true,
51142         
51143          /**
51144          * @event render
51145          * Fires when this tab is created
51146          * @param {Roo.ContentPanel} this
51147          */
51148         "render" : true
51149         
51150         
51151         
51152     });
51153     
51154
51155     
51156     
51157     if(this.autoScroll){
51158         this.resizeEl.setStyle("overflow", "auto");
51159     } else {
51160         // fix randome scrolling
51161         this.el.on('scroll', function() {
51162             Roo.log('fix random scolling');
51163             this.scrollTo('top',0); 
51164         });
51165     }
51166     content = content || this.content;
51167     if(content){
51168         this.setContent(content);
51169     }
51170     if(config && config.url){
51171         this.setUrl(this.url, this.params, this.loadOnce);
51172     }
51173     
51174     
51175     
51176     Roo.ContentPanel.superclass.constructor.call(this);
51177     
51178     if (this.view && typeof(this.view.xtype) != 'undefined') {
51179         this.view.el = this.el.appendChild(document.createElement("div"));
51180         this.view = Roo.factory(this.view); 
51181         this.view.render  &&  this.view.render(false, '');  
51182     }
51183     
51184     
51185     this.fireEvent('render', this);
51186 };
51187
51188 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51189     tabTip:'',
51190     setRegion : function(region){
51191         this.region = region;
51192         if(region){
51193            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51194         }else{
51195            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51196         } 
51197     },
51198     
51199     /**
51200      * Returns the toolbar for this Panel if one was configured. 
51201      * @return {Roo.Toolbar} 
51202      */
51203     getToolbar : function(){
51204         return this.toolbar;
51205     },
51206     
51207     setActiveState : function(active){
51208         this.active = active;
51209         if(!active){
51210             this.fireEvent("deactivate", this);
51211         }else{
51212             this.fireEvent("activate", this);
51213         }
51214     },
51215     /**
51216      * Updates this panel's element
51217      * @param {String} content The new content
51218      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51219     */
51220     setContent : function(content, loadScripts){
51221         this.el.update(content, loadScripts);
51222     },
51223
51224     ignoreResize : function(w, h){
51225         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51226             return true;
51227         }else{
51228             this.lastSize = {width: w, height: h};
51229             return false;
51230         }
51231     },
51232     /**
51233      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51234      * @return {Roo.UpdateManager} The UpdateManager
51235      */
51236     getUpdateManager : function(){
51237         return this.el.getUpdateManager();
51238     },
51239      /**
51240      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51241      * @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:
51242 <pre><code>
51243 panel.load({
51244     url: "your-url.php",
51245     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51246     callback: yourFunction,
51247     scope: yourObject, //(optional scope)
51248     discardUrl: false,
51249     nocache: false,
51250     text: "Loading...",
51251     timeout: 30,
51252     scripts: false
51253 });
51254 </code></pre>
51255      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51256      * 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.
51257      * @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}
51258      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51259      * @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.
51260      * @return {Roo.ContentPanel} this
51261      */
51262     load : function(){
51263         var um = this.el.getUpdateManager();
51264         um.update.apply(um, arguments);
51265         return this;
51266     },
51267
51268
51269     /**
51270      * 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.
51271      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51272      * @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)
51273      * @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)
51274      * @return {Roo.UpdateManager} The UpdateManager
51275      */
51276     setUrl : function(url, params, loadOnce){
51277         if(this.refreshDelegate){
51278             this.removeListener("activate", this.refreshDelegate);
51279         }
51280         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51281         this.on("activate", this.refreshDelegate);
51282         return this.el.getUpdateManager();
51283     },
51284     
51285     _handleRefresh : function(url, params, loadOnce){
51286         if(!loadOnce || !this.loaded){
51287             var updater = this.el.getUpdateManager();
51288             updater.update(url, params, this._setLoaded.createDelegate(this));
51289         }
51290     },
51291     
51292     _setLoaded : function(){
51293         this.loaded = true;
51294     }, 
51295     
51296     /**
51297      * Returns this panel's id
51298      * @return {String} 
51299      */
51300     getId : function(){
51301         return this.el.id;
51302     },
51303     
51304     /** 
51305      * Returns this panel's element - used by regiosn to add.
51306      * @return {Roo.Element} 
51307      */
51308     getEl : function(){
51309         return this.wrapEl || this.el;
51310     },
51311     
51312     adjustForComponents : function(width, height)
51313     {
51314         //Roo.log('adjustForComponents ');
51315         if(this.resizeEl != this.el){
51316             width -= this.el.getFrameWidth('lr');
51317             height -= this.el.getFrameWidth('tb');
51318         }
51319         if(this.toolbar){
51320             var te = this.toolbar.getEl();
51321             height -= te.getHeight();
51322             te.setWidth(width);
51323         }
51324         if(this.footer){
51325             var te = this.footer.getEl();
51326             Roo.log("footer:" + te.getHeight());
51327             
51328             height -= te.getHeight();
51329             te.setWidth(width);
51330         }
51331         
51332         
51333         if(this.adjustments){
51334             width += this.adjustments[0];
51335             height += this.adjustments[1];
51336         }
51337         return {"width": width, "height": height};
51338     },
51339     
51340     setSize : function(width, height){
51341         if(this.fitToFrame && !this.ignoreResize(width, height)){
51342             if(this.fitContainer && this.resizeEl != this.el){
51343                 this.el.setSize(width, height);
51344             }
51345             var size = this.adjustForComponents(width, height);
51346             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51347             this.fireEvent('resize', this, size.width, size.height);
51348         }
51349     },
51350     
51351     /**
51352      * Returns this panel's title
51353      * @return {String} 
51354      */
51355     getTitle : function(){
51356         return this.title;
51357     },
51358     
51359     /**
51360      * Set this panel's title
51361      * @param {String} title
51362      */
51363     setTitle : function(title){
51364         this.title = title;
51365         if(this.region){
51366             this.region.updatePanelTitle(this, title);
51367         }
51368     },
51369     
51370     /**
51371      * Returns true is this panel was configured to be closable
51372      * @return {Boolean} 
51373      */
51374     isClosable : function(){
51375         return this.closable;
51376     },
51377     
51378     beforeSlide : function(){
51379         this.el.clip();
51380         this.resizeEl.clip();
51381     },
51382     
51383     afterSlide : function(){
51384         this.el.unclip();
51385         this.resizeEl.unclip();
51386     },
51387     
51388     /**
51389      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51390      *   Will fail silently if the {@link #setUrl} method has not been called.
51391      *   This does not activate the panel, just updates its content.
51392      */
51393     refresh : function(){
51394         if(this.refreshDelegate){
51395            this.loaded = false;
51396            this.refreshDelegate();
51397         }
51398     },
51399     
51400     /**
51401      * Destroys this panel
51402      */
51403     destroy : function(){
51404         this.el.removeAllListeners();
51405         var tempEl = document.createElement("span");
51406         tempEl.appendChild(this.el.dom);
51407         tempEl.innerHTML = "";
51408         this.el.remove();
51409         this.el = null;
51410     },
51411     
51412     /**
51413      * form - if the content panel contains a form - this is a reference to it.
51414      * @type {Roo.form.Form}
51415      */
51416     form : false,
51417     /**
51418      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51419      *    This contains a reference to it.
51420      * @type {Roo.View}
51421      */
51422     view : false,
51423     
51424       /**
51425      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51426      * <pre><code>
51427
51428 layout.addxtype({
51429        xtype : 'Form',
51430        items: [ .... ]
51431    }
51432 );
51433
51434 </code></pre>
51435      * @param {Object} cfg Xtype definition of item to add.
51436      */
51437     
51438     addxtype : function(cfg) {
51439         // add form..
51440         if (cfg.xtype.match(/^Form$/)) {
51441             
51442             var el;
51443             //if (this.footer) {
51444             //    el = this.footer.container.insertSibling(false, 'before');
51445             //} else {
51446                 el = this.el.createChild();
51447             //}
51448
51449             this.form = new  Roo.form.Form(cfg);
51450             
51451             
51452             if ( this.form.allItems.length) this.form.render(el.dom);
51453             return this.form;
51454         }
51455         // should only have one of theses..
51456         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51457             // views.. should not be just added - used named prop 'view''
51458             
51459             cfg.el = this.el.appendChild(document.createElement("div"));
51460             // factory?
51461             
51462             var ret = new Roo.factory(cfg);
51463              
51464              ret.render && ret.render(false, ''); // render blank..
51465             this.view = ret;
51466             return ret;
51467         }
51468         return false;
51469     }
51470 });
51471
51472 /**
51473  * @class Roo.GridPanel
51474  * @extends Roo.ContentPanel
51475  * @constructor
51476  * Create a new GridPanel.
51477  * @param {Roo.grid.Grid} grid The grid for this panel
51478  * @param {String/Object} config A string to set only the panel's title, or a config object
51479  */
51480 Roo.GridPanel = function(grid, config){
51481     
51482   
51483     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51484         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51485         
51486     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51487     
51488     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51489     
51490     if(this.toolbar){
51491         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51492     }
51493     // xtype created footer. - not sure if will work as we normally have to render first..
51494     if (this.footer && !this.footer.el && this.footer.xtype) {
51495         
51496         this.footer.container = this.grid.getView().getFooterPanel(true);
51497         this.footer.dataSource = this.grid.dataSource;
51498         this.footer = Roo.factory(this.footer, Roo);
51499         
51500     }
51501     
51502     grid.monitorWindowResize = false; // turn off autosizing
51503     grid.autoHeight = false;
51504     grid.autoWidth = false;
51505     this.grid = grid;
51506     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51507 };
51508
51509 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51510     getId : function(){
51511         return this.grid.id;
51512     },
51513     
51514     /**
51515      * Returns the grid for this panel
51516      * @return {Roo.grid.Grid} 
51517      */
51518     getGrid : function(){
51519         return this.grid;    
51520     },
51521     
51522     setSize : function(width, height){
51523         if(!this.ignoreResize(width, height)){
51524             var grid = this.grid;
51525             var size = this.adjustForComponents(width, height);
51526             grid.getGridEl().setSize(size.width, size.height);
51527             grid.autoSize();
51528         }
51529     },
51530     
51531     beforeSlide : function(){
51532         this.grid.getView().scroller.clip();
51533     },
51534     
51535     afterSlide : function(){
51536         this.grid.getView().scroller.unclip();
51537     },
51538     
51539     destroy : function(){
51540         this.grid.destroy();
51541         delete this.grid;
51542         Roo.GridPanel.superclass.destroy.call(this); 
51543     }
51544 });
51545
51546
51547 /**
51548  * @class Roo.NestedLayoutPanel
51549  * @extends Roo.ContentPanel
51550  * @constructor
51551  * Create a new NestedLayoutPanel.
51552  * 
51553  * 
51554  * @param {Roo.BorderLayout} layout The layout for this panel
51555  * @param {String/Object} config A string to set only the title or a config object
51556  */
51557 Roo.NestedLayoutPanel = function(layout, config)
51558 {
51559     // construct with only one argument..
51560     /* FIXME - implement nicer consturctors
51561     if (layout.layout) {
51562         config = layout;
51563         layout = config.layout;
51564         delete config.layout;
51565     }
51566     if (layout.xtype && !layout.getEl) {
51567         // then layout needs constructing..
51568         layout = Roo.factory(layout, Roo);
51569     }
51570     */
51571     
51572     
51573     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51574     
51575     layout.monitorWindowResize = false; // turn off autosizing
51576     this.layout = layout;
51577     this.layout.getEl().addClass("x-layout-nested-layout");
51578     
51579     
51580     
51581     
51582 };
51583
51584 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51585
51586     setSize : function(width, height){
51587         if(!this.ignoreResize(width, height)){
51588             var size = this.adjustForComponents(width, height);
51589             var el = this.layout.getEl();
51590             el.setSize(size.width, size.height);
51591             var touch = el.dom.offsetWidth;
51592             this.layout.layout();
51593             // ie requires a double layout on the first pass
51594             if(Roo.isIE && !this.initialized){
51595                 this.initialized = true;
51596                 this.layout.layout();
51597             }
51598         }
51599     },
51600     
51601     // activate all subpanels if not currently active..
51602     
51603     setActiveState : function(active){
51604         this.active = active;
51605         if(!active){
51606             this.fireEvent("deactivate", this);
51607             return;
51608         }
51609         
51610         this.fireEvent("activate", this);
51611         // not sure if this should happen before or after..
51612         if (!this.layout) {
51613             return; // should not happen..
51614         }
51615         var reg = false;
51616         for (var r in this.layout.regions) {
51617             reg = this.layout.getRegion(r);
51618             if (reg.getActivePanel()) {
51619                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51620                 reg.setActivePanel(reg.getActivePanel());
51621                 continue;
51622             }
51623             if (!reg.panels.length) {
51624                 continue;
51625             }
51626             reg.showPanel(reg.getPanel(0));
51627         }
51628         
51629         
51630         
51631         
51632     },
51633     
51634     /**
51635      * Returns the nested BorderLayout for this panel
51636      * @return {Roo.BorderLayout} 
51637      */
51638     getLayout : function(){
51639         return this.layout;
51640     },
51641     
51642      /**
51643      * Adds a xtype elements to the layout of the nested panel
51644      * <pre><code>
51645
51646 panel.addxtype({
51647        xtype : 'ContentPanel',
51648        region: 'west',
51649        items: [ .... ]
51650    }
51651 );
51652
51653 panel.addxtype({
51654         xtype : 'NestedLayoutPanel',
51655         region: 'west',
51656         layout: {
51657            center: { },
51658            west: { }   
51659         },
51660         items : [ ... list of content panels or nested layout panels.. ]
51661    }
51662 );
51663 </code></pre>
51664      * @param {Object} cfg Xtype definition of item to add.
51665      */
51666     addxtype : function(cfg) {
51667         return this.layout.addxtype(cfg);
51668     
51669     }
51670 });
51671
51672 Roo.ScrollPanel = function(el, config, content){
51673     config = config || {};
51674     config.fitToFrame = true;
51675     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51676     
51677     this.el.dom.style.overflow = "hidden";
51678     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51679     this.el.removeClass("x-layout-inactive-content");
51680     this.el.on("mousewheel", this.onWheel, this);
51681
51682     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51683     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51684     up.unselectable(); down.unselectable();
51685     up.on("click", this.scrollUp, this);
51686     down.on("click", this.scrollDown, this);
51687     up.addClassOnOver("x-scroller-btn-over");
51688     down.addClassOnOver("x-scroller-btn-over");
51689     up.addClassOnClick("x-scroller-btn-click");
51690     down.addClassOnClick("x-scroller-btn-click");
51691     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51692
51693     this.resizeEl = this.el;
51694     this.el = wrap; this.up = up; this.down = down;
51695 };
51696
51697 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51698     increment : 100,
51699     wheelIncrement : 5,
51700     scrollUp : function(){
51701         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51702     },
51703
51704     scrollDown : function(){
51705         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51706     },
51707
51708     afterScroll : function(){
51709         var el = this.resizeEl;
51710         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51711         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51712         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51713     },
51714
51715     setSize : function(){
51716         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51717         this.afterScroll();
51718     },
51719
51720     onWheel : function(e){
51721         var d = e.getWheelDelta();
51722         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51723         this.afterScroll();
51724         e.stopEvent();
51725     },
51726
51727     setContent : function(content, loadScripts){
51728         this.resizeEl.update(content, loadScripts);
51729     }
51730
51731 });
51732
51733
51734
51735
51736
51737
51738
51739
51740
51741 /**
51742  * @class Roo.TreePanel
51743  * @extends Roo.ContentPanel
51744  * @constructor
51745  * Create a new TreePanel. - defaults to fit/scoll contents.
51746  * @param {String/Object} config A string to set only the panel's title, or a config object
51747  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51748  */
51749 Roo.TreePanel = function(config){
51750     var el = config.el;
51751     var tree = config.tree;
51752     delete config.tree; 
51753     delete config.el; // hopefull!
51754     
51755     // wrapper for IE7 strict & safari scroll issue
51756     
51757     var treeEl = el.createChild();
51758     config.resizeEl = treeEl;
51759     
51760     
51761     
51762     Roo.TreePanel.superclass.constructor.call(this, el, config);
51763  
51764  
51765     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51766     //console.log(tree);
51767     this.on('activate', function()
51768     {
51769         if (this.tree.rendered) {
51770             return;
51771         }
51772         //console.log('render tree');
51773         this.tree.render();
51774     });
51775     // this should not be needed.. - it's actually the 'el' that resizes?
51776     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51777     
51778     //this.on('resize',  function (cp, w, h) {
51779     //        this.tree.innerCt.setWidth(w);
51780     //        this.tree.innerCt.setHeight(h);
51781     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51782     //});
51783
51784         
51785     
51786 };
51787
51788 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51789     fitToFrame : true,
51790     autoScroll : true
51791 });
51792
51793
51794
51795
51796
51797
51798
51799
51800
51801
51802
51803 /*
51804  * Based on:
51805  * Ext JS Library 1.1.1
51806  * Copyright(c) 2006-2007, Ext JS, LLC.
51807  *
51808  * Originally Released Under LGPL - original licence link has changed is not relivant.
51809  *
51810  * Fork - LGPL
51811  * <script type="text/javascript">
51812  */
51813  
51814
51815 /**
51816  * @class Roo.ReaderLayout
51817  * @extends Roo.BorderLayout
51818  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51819  * center region containing two nested regions (a top one for a list view and one for item preview below),
51820  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51821  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51822  * expedites the setup of the overall layout and regions for this common application style.
51823  * Example:
51824  <pre><code>
51825 var reader = new Roo.ReaderLayout();
51826 var CP = Roo.ContentPanel;  // shortcut for adding
51827
51828 reader.beginUpdate();
51829 reader.add("north", new CP("north", "North"));
51830 reader.add("west", new CP("west", {title: "West"}));
51831 reader.add("east", new CP("east", {title: "East"}));
51832
51833 reader.regions.listView.add(new CP("listView", "List"));
51834 reader.regions.preview.add(new CP("preview", "Preview"));
51835 reader.endUpdate();
51836 </code></pre>
51837 * @constructor
51838 * Create a new ReaderLayout
51839 * @param {Object} config Configuration options
51840 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51841 * document.body if omitted)
51842 */
51843 Roo.ReaderLayout = function(config, renderTo){
51844     var c = config || {size:{}};
51845     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51846         north: c.north !== false ? Roo.apply({
51847             split:false,
51848             initialSize: 32,
51849             titlebar: false
51850         }, c.north) : false,
51851         west: c.west !== false ? Roo.apply({
51852             split:true,
51853             initialSize: 200,
51854             minSize: 175,
51855             maxSize: 400,
51856             titlebar: true,
51857             collapsible: true,
51858             animate: true,
51859             margins:{left:5,right:0,bottom:5,top:5},
51860             cmargins:{left:5,right:5,bottom:5,top:5}
51861         }, c.west) : false,
51862         east: c.east !== false ? Roo.apply({
51863             split:true,
51864             initialSize: 200,
51865             minSize: 175,
51866             maxSize: 400,
51867             titlebar: true,
51868             collapsible: true,
51869             animate: true,
51870             margins:{left:0,right:5,bottom:5,top:5},
51871             cmargins:{left:5,right:5,bottom:5,top:5}
51872         }, c.east) : false,
51873         center: Roo.apply({
51874             tabPosition: 'top',
51875             autoScroll:false,
51876             closeOnTab: true,
51877             titlebar:false,
51878             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51879         }, c.center)
51880     });
51881
51882     this.el.addClass('x-reader');
51883
51884     this.beginUpdate();
51885
51886     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51887         south: c.preview !== false ? Roo.apply({
51888             split:true,
51889             initialSize: 200,
51890             minSize: 100,
51891             autoScroll:true,
51892             collapsible:true,
51893             titlebar: true,
51894             cmargins:{top:5,left:0, right:0, bottom:0}
51895         }, c.preview) : false,
51896         center: Roo.apply({
51897             autoScroll:false,
51898             titlebar:false,
51899             minHeight:200
51900         }, c.listView)
51901     });
51902     this.add('center', new Roo.NestedLayoutPanel(inner,
51903             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51904
51905     this.endUpdate();
51906
51907     this.regions.preview = inner.getRegion('south');
51908     this.regions.listView = inner.getRegion('center');
51909 };
51910
51911 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51912  * Based on:
51913  * Ext JS Library 1.1.1
51914  * Copyright(c) 2006-2007, Ext JS, LLC.
51915  *
51916  * Originally Released Under LGPL - original licence link has changed is not relivant.
51917  *
51918  * Fork - LGPL
51919  * <script type="text/javascript">
51920  */
51921  
51922 /**
51923  * @class Roo.grid.Grid
51924  * @extends Roo.util.Observable
51925  * This class represents the primary interface of a component based grid control.
51926  * <br><br>Usage:<pre><code>
51927  var grid = new Roo.grid.Grid("my-container-id", {
51928      ds: myDataStore,
51929      cm: myColModel,
51930      selModel: mySelectionModel,
51931      autoSizeColumns: true,
51932      monitorWindowResize: false,
51933      trackMouseOver: true
51934  });
51935  // set any options
51936  grid.render();
51937  * </code></pre>
51938  * <b>Common Problems:</b><br/>
51939  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51940  * element will correct this<br/>
51941  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51942  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51943  * are unpredictable.<br/>
51944  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51945  * grid to calculate dimensions/offsets.<br/>
51946   * @constructor
51947  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51948  * The container MUST have some type of size defined for the grid to fill. The container will be
51949  * automatically set to position relative if it isn't already.
51950  * @param {Object} config A config object that sets properties on this grid.
51951  */
51952 Roo.grid.Grid = function(container, config){
51953         // initialize the container
51954         this.container = Roo.get(container);
51955         this.container.update("");
51956         this.container.setStyle("overflow", "hidden");
51957     this.container.addClass('x-grid-container');
51958
51959     this.id = this.container.id;
51960
51961     Roo.apply(this, config);
51962     // check and correct shorthanded configs
51963     if(this.ds){
51964         this.dataSource = this.ds;
51965         delete this.ds;
51966     }
51967     if(this.cm){
51968         this.colModel = this.cm;
51969         delete this.cm;
51970     }
51971     if(this.sm){
51972         this.selModel = this.sm;
51973         delete this.sm;
51974     }
51975
51976     if (this.selModel) {
51977         this.selModel = Roo.factory(this.selModel, Roo.grid);
51978         this.sm = this.selModel;
51979         this.sm.xmodule = this.xmodule || false;
51980     }
51981     if (typeof(this.colModel.config) == 'undefined') {
51982         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51983         this.cm = this.colModel;
51984         this.cm.xmodule = this.xmodule || false;
51985     }
51986     if (this.dataSource) {
51987         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51988         this.ds = this.dataSource;
51989         this.ds.xmodule = this.xmodule || false;
51990          
51991     }
51992     
51993     
51994     
51995     if(this.width){
51996         this.container.setWidth(this.width);
51997     }
51998
51999     if(this.height){
52000         this.container.setHeight(this.height);
52001     }
52002     /** @private */
52003         this.addEvents({
52004         // raw events
52005         /**
52006          * @event click
52007          * The raw click event for the entire grid.
52008          * @param {Roo.EventObject} e
52009          */
52010         "click" : true,
52011         /**
52012          * @event dblclick
52013          * The raw dblclick event for the entire grid.
52014          * @param {Roo.EventObject} e
52015          */
52016         "dblclick" : true,
52017         /**
52018          * @event contextmenu
52019          * The raw contextmenu event for the entire grid.
52020          * @param {Roo.EventObject} e
52021          */
52022         "contextmenu" : true,
52023         /**
52024          * @event mousedown
52025          * The raw mousedown event for the entire grid.
52026          * @param {Roo.EventObject} e
52027          */
52028         "mousedown" : true,
52029         /**
52030          * @event mouseup
52031          * The raw mouseup event for the entire grid.
52032          * @param {Roo.EventObject} e
52033          */
52034         "mouseup" : true,
52035         /**
52036          * @event mouseover
52037          * The raw mouseover event for the entire grid.
52038          * @param {Roo.EventObject} e
52039          */
52040         "mouseover" : true,
52041         /**
52042          * @event mouseout
52043          * The raw mouseout event for the entire grid.
52044          * @param {Roo.EventObject} e
52045          */
52046         "mouseout" : true,
52047         /**
52048          * @event keypress
52049          * The raw keypress event for the entire grid.
52050          * @param {Roo.EventObject} e
52051          */
52052         "keypress" : true,
52053         /**
52054          * @event keydown
52055          * The raw keydown event for the entire grid.
52056          * @param {Roo.EventObject} e
52057          */
52058         "keydown" : true,
52059
52060         // custom events
52061
52062         /**
52063          * @event cellclick
52064          * Fires when a cell is clicked
52065          * @param {Grid} this
52066          * @param {Number} rowIndex
52067          * @param {Number} columnIndex
52068          * @param {Roo.EventObject} e
52069          */
52070         "cellclick" : true,
52071         /**
52072          * @event celldblclick
52073          * Fires when a cell is double clicked
52074          * @param {Grid} this
52075          * @param {Number} rowIndex
52076          * @param {Number} columnIndex
52077          * @param {Roo.EventObject} e
52078          */
52079         "celldblclick" : true,
52080         /**
52081          * @event rowclick
52082          * Fires when a row is clicked
52083          * @param {Grid} this
52084          * @param {Number} rowIndex
52085          * @param {Roo.EventObject} e
52086          */
52087         "rowclick" : true,
52088         /**
52089          * @event rowdblclick
52090          * Fires when a row is double clicked
52091          * @param {Grid} this
52092          * @param {Number} rowIndex
52093          * @param {Roo.EventObject} e
52094          */
52095         "rowdblclick" : true,
52096         /**
52097          * @event headerclick
52098          * Fires when a header is clicked
52099          * @param {Grid} this
52100          * @param {Number} columnIndex
52101          * @param {Roo.EventObject} e
52102          */
52103         "headerclick" : true,
52104         /**
52105          * @event headerdblclick
52106          * Fires when a header cell is double clicked
52107          * @param {Grid} this
52108          * @param {Number} columnIndex
52109          * @param {Roo.EventObject} e
52110          */
52111         "headerdblclick" : true,
52112         /**
52113          * @event rowcontextmenu
52114          * Fires when a row is right clicked
52115          * @param {Grid} this
52116          * @param {Number} rowIndex
52117          * @param {Roo.EventObject} e
52118          */
52119         "rowcontextmenu" : true,
52120         /**
52121          * @event cellcontextmenu
52122          * Fires when a cell is right clicked
52123          * @param {Grid} this
52124          * @param {Number} rowIndex
52125          * @param {Number} cellIndex
52126          * @param {Roo.EventObject} e
52127          */
52128          "cellcontextmenu" : true,
52129         /**
52130          * @event headercontextmenu
52131          * Fires when a header is right clicked
52132          * @param {Grid} this
52133          * @param {Number} columnIndex
52134          * @param {Roo.EventObject} e
52135          */
52136         "headercontextmenu" : true,
52137         /**
52138          * @event bodyscroll
52139          * Fires when the body element is scrolled
52140          * @param {Number} scrollLeft
52141          * @param {Number} scrollTop
52142          */
52143         "bodyscroll" : true,
52144         /**
52145          * @event columnresize
52146          * Fires when the user resizes a column
52147          * @param {Number} columnIndex
52148          * @param {Number} newSize
52149          */
52150         "columnresize" : true,
52151         /**
52152          * @event columnmove
52153          * Fires when the user moves a column
52154          * @param {Number} oldIndex
52155          * @param {Number} newIndex
52156          */
52157         "columnmove" : true,
52158         /**
52159          * @event startdrag
52160          * Fires when row(s) start being dragged
52161          * @param {Grid} this
52162          * @param {Roo.GridDD} dd The drag drop object
52163          * @param {event} e The raw browser event
52164          */
52165         "startdrag" : true,
52166         /**
52167          * @event enddrag
52168          * Fires when a drag operation is complete
52169          * @param {Grid} this
52170          * @param {Roo.GridDD} dd The drag drop object
52171          * @param {event} e The raw browser event
52172          */
52173         "enddrag" : true,
52174         /**
52175          * @event dragdrop
52176          * Fires when dragged row(s) are dropped on a valid DD target
52177          * @param {Grid} this
52178          * @param {Roo.GridDD} dd The drag drop object
52179          * @param {String} targetId The target drag drop object
52180          * @param {event} e The raw browser event
52181          */
52182         "dragdrop" : true,
52183         /**
52184          * @event dragover
52185          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52186          * @param {Grid} this
52187          * @param {Roo.GridDD} dd The drag drop object
52188          * @param {String} targetId The target drag drop object
52189          * @param {event} e The raw browser event
52190          */
52191         "dragover" : true,
52192         /**
52193          * @event dragenter
52194          *  Fires when the dragged row(s) first cross another DD target while being dragged
52195          * @param {Grid} this
52196          * @param {Roo.GridDD} dd The drag drop object
52197          * @param {String} targetId The target drag drop object
52198          * @param {event} e The raw browser event
52199          */
52200         "dragenter" : true,
52201         /**
52202          * @event dragout
52203          * Fires when the dragged row(s) leave another DD target while being dragged
52204          * @param {Grid} this
52205          * @param {Roo.GridDD} dd The drag drop object
52206          * @param {String} targetId The target drag drop object
52207          * @param {event} e The raw browser event
52208          */
52209         "dragout" : true,
52210         /**
52211          * @event rowclass
52212          * Fires when a row is rendered, so you can change add a style to it.
52213          * @param {GridView} gridview   The grid view
52214          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52215          */
52216         'rowclass' : true,
52217
52218         /**
52219          * @event render
52220          * Fires when the grid is rendered
52221          * @param {Grid} grid
52222          */
52223         'render' : true
52224     });
52225
52226     Roo.grid.Grid.superclass.constructor.call(this);
52227 };
52228 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52229     
52230     /**
52231      * @cfg {String} ddGroup - drag drop group.
52232      */
52233
52234     /**
52235      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52236      */
52237     minColumnWidth : 25,
52238
52239     /**
52240      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52241      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52242      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52243      */
52244     autoSizeColumns : false,
52245
52246     /**
52247      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52248      */
52249     autoSizeHeaders : true,
52250
52251     /**
52252      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52253      */
52254     monitorWindowResize : true,
52255
52256     /**
52257      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52258      * rows measured to get a columns size. Default is 0 (all rows).
52259      */
52260     maxRowsToMeasure : 0,
52261
52262     /**
52263      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52264      */
52265     trackMouseOver : true,
52266
52267     /**
52268     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52269     */
52270     
52271     /**
52272     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52273     */
52274     enableDragDrop : false,
52275     
52276     /**
52277     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52278     */
52279     enableColumnMove : true,
52280     
52281     /**
52282     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52283     */
52284     enableColumnHide : true,
52285     
52286     /**
52287     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52288     */
52289     enableRowHeightSync : false,
52290     
52291     /**
52292     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52293     */
52294     stripeRows : true,
52295     
52296     /**
52297     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52298     */
52299     autoHeight : false,
52300
52301     /**
52302      * @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.
52303      */
52304     autoExpandColumn : false,
52305
52306     /**
52307     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52308     * Default is 50.
52309     */
52310     autoExpandMin : 50,
52311
52312     /**
52313     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52314     */
52315     autoExpandMax : 1000,
52316
52317     /**
52318     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52319     */
52320     view : null,
52321
52322     /**
52323     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52324     */
52325     loadMask : false,
52326     /**
52327     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52328     */
52329     dropTarget: false,
52330     
52331    
52332     
52333     // private
52334     rendered : false,
52335
52336     /**
52337     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52338     * of a fixed width. Default is false.
52339     */
52340     /**
52341     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52342     */
52343     /**
52344      * Called once after all setup has been completed and the grid is ready to be rendered.
52345      * @return {Roo.grid.Grid} this
52346      */
52347     render : function()
52348     {
52349         var c = this.container;
52350         // try to detect autoHeight/width mode
52351         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52352             this.autoHeight = true;
52353         }
52354         var view = this.getView();
52355         view.init(this);
52356
52357         c.on("click", this.onClick, this);
52358         c.on("dblclick", this.onDblClick, this);
52359         c.on("contextmenu", this.onContextMenu, this);
52360         c.on("keydown", this.onKeyDown, this);
52361         if (Roo.isTouch) {
52362             c.on("touchstart", this.onTouchStart, this);
52363         }
52364
52365         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52366
52367         this.getSelectionModel().init(this);
52368
52369         view.render();
52370
52371         if(this.loadMask){
52372             this.loadMask = new Roo.LoadMask(this.container,
52373                     Roo.apply({store:this.dataSource}, this.loadMask));
52374         }
52375         
52376         
52377         if (this.toolbar && this.toolbar.xtype) {
52378             this.toolbar.container = this.getView().getHeaderPanel(true);
52379             this.toolbar = new Roo.Toolbar(this.toolbar);
52380         }
52381         if (this.footer && this.footer.xtype) {
52382             this.footer.dataSource = this.getDataSource();
52383             this.footer.container = this.getView().getFooterPanel(true);
52384             this.footer = Roo.factory(this.footer, Roo);
52385         }
52386         if (this.dropTarget && this.dropTarget.xtype) {
52387             delete this.dropTarget.xtype;
52388             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52389         }
52390         
52391         
52392         this.rendered = true;
52393         this.fireEvent('render', this);
52394         return this;
52395     },
52396
52397         /**
52398          * Reconfigures the grid to use a different Store and Column Model.
52399          * The View will be bound to the new objects and refreshed.
52400          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52401          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52402          */
52403     reconfigure : function(dataSource, colModel){
52404         if(this.loadMask){
52405             this.loadMask.destroy();
52406             this.loadMask = new Roo.LoadMask(this.container,
52407                     Roo.apply({store:dataSource}, this.loadMask));
52408         }
52409         this.view.bind(dataSource, colModel);
52410         this.dataSource = dataSource;
52411         this.colModel = colModel;
52412         this.view.refresh(true);
52413     },
52414
52415     // private
52416     onKeyDown : function(e){
52417         this.fireEvent("keydown", e);
52418     },
52419
52420     /**
52421      * Destroy this grid.
52422      * @param {Boolean} removeEl True to remove the element
52423      */
52424     destroy : function(removeEl, keepListeners){
52425         if(this.loadMask){
52426             this.loadMask.destroy();
52427         }
52428         var c = this.container;
52429         c.removeAllListeners();
52430         this.view.destroy();
52431         this.colModel.purgeListeners();
52432         if(!keepListeners){
52433             this.purgeListeners();
52434         }
52435         c.update("");
52436         if(removeEl === true){
52437             c.remove();
52438         }
52439     },
52440
52441     // private
52442     processEvent : function(name, e){
52443         // does this fire select???
52444         Roo.log('grid:processEvent '  + name);
52445         
52446         if (name != 'touchstart' ) {
52447             this.fireEvent(name, e);    
52448         }
52449         
52450         var t = e.getTarget();
52451         var v = this.view;
52452         var header = v.findHeaderIndex(t);
52453         if(header !== false){
52454             var ename = name == 'touchstart' ? 'click' : name;
52455              
52456             this.fireEvent("header" + ename, this, header, e);
52457         }else{
52458             var row = v.findRowIndex(t);
52459             var cell = v.findCellIndex(t);
52460             if (name == 'touchstart') {
52461                 // first touch is always a click.
52462                 // hopefull this happens after selection is updated.?
52463                 name = false;
52464                 
52465                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52466                     var cs = this.selModel.getSelectedCell();
52467                     if (row == cs[0] && cell == cs[1]){
52468                         name = 'dblclick';
52469                     }
52470                 }
52471                 if (typeof(this.selModel.getSelections) != 'undefined') {
52472                     var cs = this.selModel.getSelections();
52473                     var ds = this.dataSource;
52474                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52475                         name = 'dblclick';
52476                     }
52477                 }
52478                 if (!name) {
52479                     return;
52480                 }
52481             }
52482             
52483             
52484             if(row !== false){
52485                 this.fireEvent("row" + name, this, row, e);
52486                 if(cell !== false){
52487                     this.fireEvent("cell" + name, this, row, cell, e);
52488                 }
52489             }
52490         }
52491     },
52492
52493     // private
52494     onClick : function(e){
52495         this.processEvent("click", e);
52496     },
52497    // private
52498     onTouchStart : function(e){
52499         this.processEvent("touchstart", e);
52500     },
52501
52502     // private
52503     onContextMenu : function(e, t){
52504         this.processEvent("contextmenu", e);
52505     },
52506
52507     // private
52508     onDblClick : function(e){
52509         this.processEvent("dblclick", e);
52510     },
52511
52512     // private
52513     walkCells : function(row, col, step, fn, scope){
52514         var cm = this.colModel, clen = cm.getColumnCount();
52515         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52516         if(step < 0){
52517             if(col < 0){
52518                 row--;
52519                 first = false;
52520             }
52521             while(row >= 0){
52522                 if(!first){
52523                     col = clen-1;
52524                 }
52525                 first = false;
52526                 while(col >= 0){
52527                     if(fn.call(scope || this, row, col, cm) === true){
52528                         return [row, col];
52529                     }
52530                     col--;
52531                 }
52532                 row--;
52533             }
52534         } else {
52535             if(col >= clen){
52536                 row++;
52537                 first = false;
52538             }
52539             while(row < rlen){
52540                 if(!first){
52541                     col = 0;
52542                 }
52543                 first = false;
52544                 while(col < clen){
52545                     if(fn.call(scope || this, row, col, cm) === true){
52546                         return [row, col];
52547                     }
52548                     col++;
52549                 }
52550                 row++;
52551             }
52552         }
52553         return null;
52554     },
52555
52556     // private
52557     getSelections : function(){
52558         return this.selModel.getSelections();
52559     },
52560
52561     /**
52562      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52563      * but if manual update is required this method will initiate it.
52564      */
52565     autoSize : function(){
52566         if(this.rendered){
52567             this.view.layout();
52568             if(this.view.adjustForScroll){
52569                 this.view.adjustForScroll();
52570             }
52571         }
52572     },
52573
52574     /**
52575      * Returns the grid's underlying element.
52576      * @return {Element} The element
52577      */
52578     getGridEl : function(){
52579         return this.container;
52580     },
52581
52582     // private for compatibility, overridden by editor grid
52583     stopEditing : function(){},
52584
52585     /**
52586      * Returns the grid's SelectionModel.
52587      * @return {SelectionModel}
52588      */
52589     getSelectionModel : function(){
52590         if(!this.selModel){
52591             this.selModel = new Roo.grid.RowSelectionModel();
52592         }
52593         return this.selModel;
52594     },
52595
52596     /**
52597      * Returns the grid's DataSource.
52598      * @return {DataSource}
52599      */
52600     getDataSource : function(){
52601         return this.dataSource;
52602     },
52603
52604     /**
52605      * Returns the grid's ColumnModel.
52606      * @return {ColumnModel}
52607      */
52608     getColumnModel : function(){
52609         return this.colModel;
52610     },
52611
52612     /**
52613      * Returns the grid's GridView object.
52614      * @return {GridView}
52615      */
52616     getView : function(){
52617         if(!this.view){
52618             this.view = new Roo.grid.GridView(this.viewConfig);
52619         }
52620         return this.view;
52621     },
52622     /**
52623      * Called to get grid's drag proxy text, by default returns this.ddText.
52624      * @return {String}
52625      */
52626     getDragDropText : function(){
52627         var count = this.selModel.getCount();
52628         return String.format(this.ddText, count, count == 1 ? '' : 's');
52629     }
52630 });
52631 /**
52632  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52633  * %0 is replaced with the number of selected rows.
52634  * @type String
52635  */
52636 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52637  * Based on:
52638  * Ext JS Library 1.1.1
52639  * Copyright(c) 2006-2007, Ext JS, LLC.
52640  *
52641  * Originally Released Under LGPL - original licence link has changed is not relivant.
52642  *
52643  * Fork - LGPL
52644  * <script type="text/javascript">
52645  */
52646  
52647 Roo.grid.AbstractGridView = function(){
52648         this.grid = null;
52649         
52650         this.events = {
52651             "beforerowremoved" : true,
52652             "beforerowsinserted" : true,
52653             "beforerefresh" : true,
52654             "rowremoved" : true,
52655             "rowsinserted" : true,
52656             "rowupdated" : true,
52657             "refresh" : true
52658         };
52659     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52660 };
52661
52662 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52663     rowClass : "x-grid-row",
52664     cellClass : "x-grid-cell",
52665     tdClass : "x-grid-td",
52666     hdClass : "x-grid-hd",
52667     splitClass : "x-grid-hd-split",
52668     
52669     init: function(grid){
52670         this.grid = grid;
52671                 var cid = this.grid.getGridEl().id;
52672         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52673         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52674         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52675         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52676         },
52677         
52678     getColumnRenderers : function(){
52679         var renderers = [];
52680         var cm = this.grid.colModel;
52681         var colCount = cm.getColumnCount();
52682         for(var i = 0; i < colCount; i++){
52683             renderers[i] = cm.getRenderer(i);
52684         }
52685         return renderers;
52686     },
52687     
52688     getColumnIds : function(){
52689         var ids = [];
52690         var cm = this.grid.colModel;
52691         var colCount = cm.getColumnCount();
52692         for(var i = 0; i < colCount; i++){
52693             ids[i] = cm.getColumnId(i);
52694         }
52695         return ids;
52696     },
52697     
52698     getDataIndexes : function(){
52699         if(!this.indexMap){
52700             this.indexMap = this.buildIndexMap();
52701         }
52702         return this.indexMap.colToData;
52703     },
52704     
52705     getColumnIndexByDataIndex : function(dataIndex){
52706         if(!this.indexMap){
52707             this.indexMap = this.buildIndexMap();
52708         }
52709         return this.indexMap.dataToCol[dataIndex];
52710     },
52711     
52712     /**
52713      * Set a css style for a column dynamically. 
52714      * @param {Number} colIndex The index of the column
52715      * @param {String} name The css property name
52716      * @param {String} value The css value
52717      */
52718     setCSSStyle : function(colIndex, name, value){
52719         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52720         Roo.util.CSS.updateRule(selector, name, value);
52721     },
52722     
52723     generateRules : function(cm){
52724         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52725         Roo.util.CSS.removeStyleSheet(rulesId);
52726         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52727             var cid = cm.getColumnId(i);
52728             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52729                          this.tdSelector, cid, " {\n}\n",
52730                          this.hdSelector, cid, " {\n}\n",
52731                          this.splitSelector, cid, " {\n}\n");
52732         }
52733         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52734     }
52735 });/*
52736  * Based on:
52737  * Ext JS Library 1.1.1
52738  * Copyright(c) 2006-2007, Ext JS, LLC.
52739  *
52740  * Originally Released Under LGPL - original licence link has changed is not relivant.
52741  *
52742  * Fork - LGPL
52743  * <script type="text/javascript">
52744  */
52745
52746 // private
52747 // This is a support class used internally by the Grid components
52748 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52749     this.grid = grid;
52750     this.view = grid.getView();
52751     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52752     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52753     if(hd2){
52754         this.setHandleElId(Roo.id(hd));
52755         this.setOuterHandleElId(Roo.id(hd2));
52756     }
52757     this.scroll = false;
52758 };
52759 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52760     maxDragWidth: 120,
52761     getDragData : function(e){
52762         var t = Roo.lib.Event.getTarget(e);
52763         var h = this.view.findHeaderCell(t);
52764         if(h){
52765             return {ddel: h.firstChild, header:h};
52766         }
52767         return false;
52768     },
52769
52770     onInitDrag : function(e){
52771         this.view.headersDisabled = true;
52772         var clone = this.dragData.ddel.cloneNode(true);
52773         clone.id = Roo.id();
52774         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52775         this.proxy.update(clone);
52776         return true;
52777     },
52778
52779     afterValidDrop : function(){
52780         var v = this.view;
52781         setTimeout(function(){
52782             v.headersDisabled = false;
52783         }, 50);
52784     },
52785
52786     afterInvalidDrop : function(){
52787         var v = this.view;
52788         setTimeout(function(){
52789             v.headersDisabled = false;
52790         }, 50);
52791     }
52792 });
52793 /*
52794  * Based on:
52795  * Ext JS Library 1.1.1
52796  * Copyright(c) 2006-2007, Ext JS, LLC.
52797  *
52798  * Originally Released Under LGPL - original licence link has changed is not relivant.
52799  *
52800  * Fork - LGPL
52801  * <script type="text/javascript">
52802  */
52803 // private
52804 // This is a support class used internally by the Grid components
52805 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52806     this.grid = grid;
52807     this.view = grid.getView();
52808     // split the proxies so they don't interfere with mouse events
52809     this.proxyTop = Roo.DomHelper.append(document.body, {
52810         cls:"col-move-top", html:"&#160;"
52811     }, true);
52812     this.proxyBottom = Roo.DomHelper.append(document.body, {
52813         cls:"col-move-bottom", html:"&#160;"
52814     }, true);
52815     this.proxyTop.hide = this.proxyBottom.hide = function(){
52816         this.setLeftTop(-100,-100);
52817         this.setStyle("visibility", "hidden");
52818     };
52819     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52820     // temporarily disabled
52821     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52822     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52823 };
52824 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52825     proxyOffsets : [-4, -9],
52826     fly: Roo.Element.fly,
52827
52828     getTargetFromEvent : function(e){
52829         var t = Roo.lib.Event.getTarget(e);
52830         var cindex = this.view.findCellIndex(t);
52831         if(cindex !== false){
52832             return this.view.getHeaderCell(cindex);
52833         }
52834         return null;
52835     },
52836
52837     nextVisible : function(h){
52838         var v = this.view, cm = this.grid.colModel;
52839         h = h.nextSibling;
52840         while(h){
52841             if(!cm.isHidden(v.getCellIndex(h))){
52842                 return h;
52843             }
52844             h = h.nextSibling;
52845         }
52846         return null;
52847     },
52848
52849     prevVisible : function(h){
52850         var v = this.view, cm = this.grid.colModel;
52851         h = h.prevSibling;
52852         while(h){
52853             if(!cm.isHidden(v.getCellIndex(h))){
52854                 return h;
52855             }
52856             h = h.prevSibling;
52857         }
52858         return null;
52859     },
52860
52861     positionIndicator : function(h, n, e){
52862         var x = Roo.lib.Event.getPageX(e);
52863         var r = Roo.lib.Dom.getRegion(n.firstChild);
52864         var px, pt, py = r.top + this.proxyOffsets[1];
52865         if((r.right - x) <= (r.right-r.left)/2){
52866             px = r.right+this.view.borderWidth;
52867             pt = "after";
52868         }else{
52869             px = r.left;
52870             pt = "before";
52871         }
52872         var oldIndex = this.view.getCellIndex(h);
52873         var newIndex = this.view.getCellIndex(n);
52874
52875         if(this.grid.colModel.isFixed(newIndex)){
52876             return false;
52877         }
52878
52879         var locked = this.grid.colModel.isLocked(newIndex);
52880
52881         if(pt == "after"){
52882             newIndex++;
52883         }
52884         if(oldIndex < newIndex){
52885             newIndex--;
52886         }
52887         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52888             return false;
52889         }
52890         px +=  this.proxyOffsets[0];
52891         this.proxyTop.setLeftTop(px, py);
52892         this.proxyTop.show();
52893         if(!this.bottomOffset){
52894             this.bottomOffset = this.view.mainHd.getHeight();
52895         }
52896         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52897         this.proxyBottom.show();
52898         return pt;
52899     },
52900
52901     onNodeEnter : function(n, dd, e, data){
52902         if(data.header != n){
52903             this.positionIndicator(data.header, n, e);
52904         }
52905     },
52906
52907     onNodeOver : function(n, dd, e, data){
52908         var result = false;
52909         if(data.header != n){
52910             result = this.positionIndicator(data.header, n, e);
52911         }
52912         if(!result){
52913             this.proxyTop.hide();
52914             this.proxyBottom.hide();
52915         }
52916         return result ? this.dropAllowed : this.dropNotAllowed;
52917     },
52918
52919     onNodeOut : function(n, dd, e, data){
52920         this.proxyTop.hide();
52921         this.proxyBottom.hide();
52922     },
52923
52924     onNodeDrop : function(n, dd, e, data){
52925         var h = data.header;
52926         if(h != n){
52927             var cm = this.grid.colModel;
52928             var x = Roo.lib.Event.getPageX(e);
52929             var r = Roo.lib.Dom.getRegion(n.firstChild);
52930             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52931             var oldIndex = this.view.getCellIndex(h);
52932             var newIndex = this.view.getCellIndex(n);
52933             var locked = cm.isLocked(newIndex);
52934             if(pt == "after"){
52935                 newIndex++;
52936             }
52937             if(oldIndex < newIndex){
52938                 newIndex--;
52939             }
52940             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52941                 return false;
52942             }
52943             cm.setLocked(oldIndex, locked, true);
52944             cm.moveColumn(oldIndex, newIndex);
52945             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52946             return true;
52947         }
52948         return false;
52949     }
52950 });
52951 /*
52952  * Based on:
52953  * Ext JS Library 1.1.1
52954  * Copyright(c) 2006-2007, Ext JS, LLC.
52955  *
52956  * Originally Released Under LGPL - original licence link has changed is not relivant.
52957  *
52958  * Fork - LGPL
52959  * <script type="text/javascript">
52960  */
52961   
52962 /**
52963  * @class Roo.grid.GridView
52964  * @extends Roo.util.Observable
52965  *
52966  * @constructor
52967  * @param {Object} config
52968  */
52969 Roo.grid.GridView = function(config){
52970     Roo.grid.GridView.superclass.constructor.call(this);
52971     this.el = null;
52972
52973     Roo.apply(this, config);
52974 };
52975
52976 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52977
52978     unselectable :  'unselectable="on"',
52979     unselectableCls :  'x-unselectable',
52980     
52981     
52982     rowClass : "x-grid-row",
52983
52984     cellClass : "x-grid-col",
52985
52986     tdClass : "x-grid-td",
52987
52988     hdClass : "x-grid-hd",
52989
52990     splitClass : "x-grid-split",
52991
52992     sortClasses : ["sort-asc", "sort-desc"],
52993
52994     enableMoveAnim : false,
52995
52996     hlColor: "C3DAF9",
52997
52998     dh : Roo.DomHelper,
52999
53000     fly : Roo.Element.fly,
53001
53002     css : Roo.util.CSS,
53003
53004     borderWidth: 1,
53005
53006     splitOffset: 3,
53007
53008     scrollIncrement : 22,
53009
53010     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53011
53012     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53013
53014     bind : function(ds, cm){
53015         if(this.ds){
53016             this.ds.un("load", this.onLoad, this);
53017             this.ds.un("datachanged", this.onDataChange, this);
53018             this.ds.un("add", this.onAdd, this);
53019             this.ds.un("remove", this.onRemove, this);
53020             this.ds.un("update", this.onUpdate, this);
53021             this.ds.un("clear", this.onClear, this);
53022         }
53023         if(ds){
53024             ds.on("load", this.onLoad, this);
53025             ds.on("datachanged", this.onDataChange, this);
53026             ds.on("add", this.onAdd, this);
53027             ds.on("remove", this.onRemove, this);
53028             ds.on("update", this.onUpdate, this);
53029             ds.on("clear", this.onClear, this);
53030         }
53031         this.ds = ds;
53032
53033         if(this.cm){
53034             this.cm.un("widthchange", this.onColWidthChange, this);
53035             this.cm.un("headerchange", this.onHeaderChange, this);
53036             this.cm.un("hiddenchange", this.onHiddenChange, this);
53037             this.cm.un("columnmoved", this.onColumnMove, this);
53038             this.cm.un("columnlockchange", this.onColumnLock, this);
53039         }
53040         if(cm){
53041             this.generateRules(cm);
53042             cm.on("widthchange", this.onColWidthChange, this);
53043             cm.on("headerchange", this.onHeaderChange, this);
53044             cm.on("hiddenchange", this.onHiddenChange, this);
53045             cm.on("columnmoved", this.onColumnMove, this);
53046             cm.on("columnlockchange", this.onColumnLock, this);
53047         }
53048         this.cm = cm;
53049     },
53050
53051     init: function(grid){
53052         Roo.grid.GridView.superclass.init.call(this, grid);
53053
53054         this.bind(grid.dataSource, grid.colModel);
53055
53056         grid.on("headerclick", this.handleHeaderClick, this);
53057
53058         if(grid.trackMouseOver){
53059             grid.on("mouseover", this.onRowOver, this);
53060             grid.on("mouseout", this.onRowOut, this);
53061         }
53062         grid.cancelTextSelection = function(){};
53063         this.gridId = grid.id;
53064
53065         var tpls = this.templates || {};
53066
53067         if(!tpls.master){
53068             tpls.master = new Roo.Template(
53069                '<div class="x-grid" hidefocus="true">',
53070                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53071                   '<div class="x-grid-topbar"></div>',
53072                   '<div class="x-grid-scroller"><div></div></div>',
53073                   '<div class="x-grid-locked">',
53074                       '<div class="x-grid-header">{lockedHeader}</div>',
53075                       '<div class="x-grid-body">{lockedBody}</div>',
53076                   "</div>",
53077                   '<div class="x-grid-viewport">',
53078                       '<div class="x-grid-header">{header}</div>',
53079                       '<div class="x-grid-body">{body}</div>',
53080                   "</div>",
53081                   '<div class="x-grid-bottombar"></div>',
53082                  
53083                   '<div class="x-grid-resize-proxy">&#160;</div>',
53084                "</div>"
53085             );
53086             tpls.master.disableformats = true;
53087         }
53088
53089         if(!tpls.header){
53090             tpls.header = new Roo.Template(
53091                '<table border="0" cellspacing="0" cellpadding="0">',
53092                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53093                "</table>{splits}"
53094             );
53095             tpls.header.disableformats = true;
53096         }
53097         tpls.header.compile();
53098
53099         if(!tpls.hcell){
53100             tpls.hcell = new Roo.Template(
53101                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53102                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53103                 "</div></td>"
53104              );
53105              tpls.hcell.disableFormats = true;
53106         }
53107         tpls.hcell.compile();
53108
53109         if(!tpls.hsplit){
53110             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53111                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53112             tpls.hsplit.disableFormats = true;
53113         }
53114         tpls.hsplit.compile();
53115
53116         if(!tpls.body){
53117             tpls.body = new Roo.Template(
53118                '<table border="0" cellspacing="0" cellpadding="0">',
53119                "<tbody>{rows}</tbody>",
53120                "</table>"
53121             );
53122             tpls.body.disableFormats = true;
53123         }
53124         tpls.body.compile();
53125
53126         if(!tpls.row){
53127             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53128             tpls.row.disableFormats = true;
53129         }
53130         tpls.row.compile();
53131
53132         if(!tpls.cell){
53133             tpls.cell = new Roo.Template(
53134                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53135                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53136                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53137                 "</td>"
53138             );
53139             tpls.cell.disableFormats = true;
53140         }
53141         tpls.cell.compile();
53142
53143         this.templates = tpls;
53144     },
53145
53146     // remap these for backwards compat
53147     onColWidthChange : function(){
53148         this.updateColumns.apply(this, arguments);
53149     },
53150     onHeaderChange : function(){
53151         this.updateHeaders.apply(this, arguments);
53152     }, 
53153     onHiddenChange : function(){
53154         this.handleHiddenChange.apply(this, arguments);
53155     },
53156     onColumnMove : function(){
53157         this.handleColumnMove.apply(this, arguments);
53158     },
53159     onColumnLock : function(){
53160         this.handleLockChange.apply(this, arguments);
53161     },
53162
53163     onDataChange : function(){
53164         this.refresh();
53165         this.updateHeaderSortState();
53166     },
53167
53168     onClear : function(){
53169         this.refresh();
53170     },
53171
53172     onUpdate : function(ds, record){
53173         this.refreshRow(record);
53174     },
53175
53176     refreshRow : function(record){
53177         var ds = this.ds, index;
53178         if(typeof record == 'number'){
53179             index = record;
53180             record = ds.getAt(index);
53181         }else{
53182             index = ds.indexOf(record);
53183         }
53184         this.insertRows(ds, index, index, true);
53185         this.onRemove(ds, record, index+1, true);
53186         this.syncRowHeights(index, index);
53187         this.layout();
53188         this.fireEvent("rowupdated", this, index, record);
53189     },
53190
53191     onAdd : function(ds, records, index){
53192         this.insertRows(ds, index, index + (records.length-1));
53193     },
53194
53195     onRemove : function(ds, record, index, isUpdate){
53196         if(isUpdate !== true){
53197             this.fireEvent("beforerowremoved", this, index, record);
53198         }
53199         var bt = this.getBodyTable(), lt = this.getLockedTable();
53200         if(bt.rows[index]){
53201             bt.firstChild.removeChild(bt.rows[index]);
53202         }
53203         if(lt.rows[index]){
53204             lt.firstChild.removeChild(lt.rows[index]);
53205         }
53206         if(isUpdate !== true){
53207             this.stripeRows(index);
53208             this.syncRowHeights(index, index);
53209             this.layout();
53210             this.fireEvent("rowremoved", this, index, record);
53211         }
53212     },
53213
53214     onLoad : function(){
53215         this.scrollToTop();
53216     },
53217
53218     /**
53219      * Scrolls the grid to the top
53220      */
53221     scrollToTop : function(){
53222         if(this.scroller){
53223             this.scroller.dom.scrollTop = 0;
53224             this.syncScroll();
53225         }
53226     },
53227
53228     /**
53229      * Gets a panel in the header of the grid that can be used for toolbars etc.
53230      * After modifying the contents of this panel a call to grid.autoSize() may be
53231      * required to register any changes in size.
53232      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53233      * @return Roo.Element
53234      */
53235     getHeaderPanel : function(doShow){
53236         if(doShow){
53237             this.headerPanel.show();
53238         }
53239         return this.headerPanel;
53240     },
53241
53242     /**
53243      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53244      * After modifying the contents of this panel a call to grid.autoSize() may be
53245      * required to register any changes in size.
53246      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53247      * @return Roo.Element
53248      */
53249     getFooterPanel : function(doShow){
53250         if(doShow){
53251             this.footerPanel.show();
53252         }
53253         return this.footerPanel;
53254     },
53255
53256     initElements : function(){
53257         var E = Roo.Element;
53258         var el = this.grid.getGridEl().dom.firstChild;
53259         var cs = el.childNodes;
53260
53261         this.el = new E(el);
53262         
53263          this.focusEl = new E(el.firstChild);
53264         this.focusEl.swallowEvent("click", true);
53265         
53266         this.headerPanel = new E(cs[1]);
53267         this.headerPanel.enableDisplayMode("block");
53268
53269         this.scroller = new E(cs[2]);
53270         this.scrollSizer = new E(this.scroller.dom.firstChild);
53271
53272         this.lockedWrap = new E(cs[3]);
53273         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53274         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53275
53276         this.mainWrap = new E(cs[4]);
53277         this.mainHd = new E(this.mainWrap.dom.firstChild);
53278         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53279
53280         this.footerPanel = new E(cs[5]);
53281         this.footerPanel.enableDisplayMode("block");
53282
53283         this.resizeProxy = new E(cs[6]);
53284
53285         this.headerSelector = String.format(
53286            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53287            this.lockedHd.id, this.mainHd.id
53288         );
53289
53290         this.splitterSelector = String.format(
53291            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53292            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53293         );
53294     },
53295     idToCssName : function(s)
53296     {
53297         return s.replace(/[^a-z0-9]+/ig, '-');
53298     },
53299
53300     getHeaderCell : function(index){
53301         return Roo.DomQuery.select(this.headerSelector)[index];
53302     },
53303
53304     getHeaderCellMeasure : function(index){
53305         return this.getHeaderCell(index).firstChild;
53306     },
53307
53308     getHeaderCellText : function(index){
53309         return this.getHeaderCell(index).firstChild.firstChild;
53310     },
53311
53312     getLockedTable : function(){
53313         return this.lockedBody.dom.firstChild;
53314     },
53315
53316     getBodyTable : function(){
53317         return this.mainBody.dom.firstChild;
53318     },
53319
53320     getLockedRow : function(index){
53321         return this.getLockedTable().rows[index];
53322     },
53323
53324     getRow : function(index){
53325         return this.getBodyTable().rows[index];
53326     },
53327
53328     getRowComposite : function(index){
53329         if(!this.rowEl){
53330             this.rowEl = new Roo.CompositeElementLite();
53331         }
53332         var els = [], lrow, mrow;
53333         if(lrow = this.getLockedRow(index)){
53334             els.push(lrow);
53335         }
53336         if(mrow = this.getRow(index)){
53337             els.push(mrow);
53338         }
53339         this.rowEl.elements = els;
53340         return this.rowEl;
53341     },
53342     /**
53343      * Gets the 'td' of the cell
53344      * 
53345      * @param {Integer} rowIndex row to select
53346      * @param {Integer} colIndex column to select
53347      * 
53348      * @return {Object} 
53349      */
53350     getCell : function(rowIndex, colIndex){
53351         var locked = this.cm.getLockedCount();
53352         var source;
53353         if(colIndex < locked){
53354             source = this.lockedBody.dom.firstChild;
53355         }else{
53356             source = this.mainBody.dom.firstChild;
53357             colIndex -= locked;
53358         }
53359         return source.rows[rowIndex].childNodes[colIndex];
53360     },
53361
53362     getCellText : function(rowIndex, colIndex){
53363         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53364     },
53365
53366     getCellBox : function(cell){
53367         var b = this.fly(cell).getBox();
53368         if(Roo.isOpera){ // opera fails to report the Y
53369             b.y = cell.offsetTop + this.mainBody.getY();
53370         }
53371         return b;
53372     },
53373
53374     getCellIndex : function(cell){
53375         var id = String(cell.className).match(this.cellRE);
53376         if(id){
53377             return parseInt(id[1], 10);
53378         }
53379         return 0;
53380     },
53381
53382     findHeaderIndex : function(n){
53383         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53384         return r ? this.getCellIndex(r) : false;
53385     },
53386
53387     findHeaderCell : function(n){
53388         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53389         return r ? r : false;
53390     },
53391
53392     findRowIndex : function(n){
53393         if(!n){
53394             return false;
53395         }
53396         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53397         return r ? r.rowIndex : false;
53398     },
53399
53400     findCellIndex : function(node){
53401         var stop = this.el.dom;
53402         while(node && node != stop){
53403             if(this.findRE.test(node.className)){
53404                 return this.getCellIndex(node);
53405             }
53406             node = node.parentNode;
53407         }
53408         return false;
53409     },
53410
53411     getColumnId : function(index){
53412         return this.cm.getColumnId(index);
53413     },
53414
53415     getSplitters : function()
53416     {
53417         if(this.splitterSelector){
53418            return Roo.DomQuery.select(this.splitterSelector);
53419         }else{
53420             return null;
53421       }
53422     },
53423
53424     getSplitter : function(index){
53425         return this.getSplitters()[index];
53426     },
53427
53428     onRowOver : function(e, t){
53429         var row;
53430         if((row = this.findRowIndex(t)) !== false){
53431             this.getRowComposite(row).addClass("x-grid-row-over");
53432         }
53433     },
53434
53435     onRowOut : function(e, t){
53436         var row;
53437         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53438             this.getRowComposite(row).removeClass("x-grid-row-over");
53439         }
53440     },
53441
53442     renderHeaders : function(){
53443         var cm = this.cm;
53444         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53445         var cb = [], lb = [], sb = [], lsb = [], p = {};
53446         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53447             p.cellId = "x-grid-hd-0-" + i;
53448             p.splitId = "x-grid-csplit-0-" + i;
53449             p.id = cm.getColumnId(i);
53450             p.title = cm.getColumnTooltip(i) || "";
53451             p.value = cm.getColumnHeader(i) || "";
53452             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53453             if(!cm.isLocked(i)){
53454                 cb[cb.length] = ct.apply(p);
53455                 sb[sb.length] = st.apply(p);
53456             }else{
53457                 lb[lb.length] = ct.apply(p);
53458                 lsb[lsb.length] = st.apply(p);
53459             }
53460         }
53461         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53462                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53463     },
53464
53465     updateHeaders : function(){
53466         var html = this.renderHeaders();
53467         this.lockedHd.update(html[0]);
53468         this.mainHd.update(html[1]);
53469     },
53470
53471     /**
53472      * Focuses the specified row.
53473      * @param {Number} row The row index
53474      */
53475     focusRow : function(row)
53476     {
53477         //Roo.log('GridView.focusRow');
53478         var x = this.scroller.dom.scrollLeft;
53479         this.focusCell(row, 0, false);
53480         this.scroller.dom.scrollLeft = x;
53481     },
53482
53483     /**
53484      * Focuses the specified cell.
53485      * @param {Number} row The row index
53486      * @param {Number} col The column index
53487      * @param {Boolean} hscroll false to disable horizontal scrolling
53488      */
53489     focusCell : function(row, col, hscroll)
53490     {
53491         //Roo.log('GridView.focusCell');
53492         var el = this.ensureVisible(row, col, hscroll);
53493         this.focusEl.alignTo(el, "tl-tl");
53494         if(Roo.isGecko){
53495             this.focusEl.focus();
53496         }else{
53497             this.focusEl.focus.defer(1, this.focusEl);
53498         }
53499     },
53500
53501     /**
53502      * Scrolls the specified cell into view
53503      * @param {Number} row The row index
53504      * @param {Number} col The column index
53505      * @param {Boolean} hscroll false to disable horizontal scrolling
53506      */
53507     ensureVisible : function(row, col, hscroll)
53508     {
53509         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53510         //return null; //disable for testing.
53511         if(typeof row != "number"){
53512             row = row.rowIndex;
53513         }
53514         if(row < 0 && row >= this.ds.getCount()){
53515             return  null;
53516         }
53517         col = (col !== undefined ? col : 0);
53518         var cm = this.grid.colModel;
53519         while(cm.isHidden(col)){
53520             col++;
53521         }
53522
53523         var el = this.getCell(row, col);
53524         if(!el){
53525             return null;
53526         }
53527         var c = this.scroller.dom;
53528
53529         var ctop = parseInt(el.offsetTop, 10);
53530         var cleft = parseInt(el.offsetLeft, 10);
53531         var cbot = ctop + el.offsetHeight;
53532         var cright = cleft + el.offsetWidth;
53533         
53534         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53535         var stop = parseInt(c.scrollTop, 10);
53536         var sleft = parseInt(c.scrollLeft, 10);
53537         var sbot = stop + ch;
53538         var sright = sleft + c.clientWidth;
53539         /*
53540         Roo.log('GridView.ensureVisible:' +
53541                 ' ctop:' + ctop +
53542                 ' c.clientHeight:' + c.clientHeight +
53543                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53544                 ' stop:' + stop +
53545                 ' cbot:' + cbot +
53546                 ' sbot:' + sbot +
53547                 ' ch:' + ch  
53548                 );
53549         */
53550         if(ctop < stop){
53551              c.scrollTop = ctop;
53552             //Roo.log("set scrolltop to ctop DISABLE?");
53553         }else if(cbot > sbot){
53554             //Roo.log("set scrolltop to cbot-ch");
53555             c.scrollTop = cbot-ch;
53556         }
53557         
53558         if(hscroll !== false){
53559             if(cleft < sleft){
53560                 c.scrollLeft = cleft;
53561             }else if(cright > sright){
53562                 c.scrollLeft = cright-c.clientWidth;
53563             }
53564         }
53565          
53566         return el;
53567     },
53568
53569     updateColumns : function(){
53570         this.grid.stopEditing();
53571         var cm = this.grid.colModel, colIds = this.getColumnIds();
53572         //var totalWidth = cm.getTotalWidth();
53573         var pos = 0;
53574         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53575             //if(cm.isHidden(i)) continue;
53576             var w = cm.getColumnWidth(i);
53577             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53578             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53579         }
53580         this.updateSplitters();
53581     },
53582
53583     generateRules : function(cm){
53584         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53585         Roo.util.CSS.removeStyleSheet(rulesId);
53586         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53587             var cid = cm.getColumnId(i);
53588             var align = '';
53589             if(cm.config[i].align){
53590                 align = 'text-align:'+cm.config[i].align+';';
53591             }
53592             var hidden = '';
53593             if(cm.isHidden(i)){
53594                 hidden = 'display:none;';
53595             }
53596             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53597             ruleBuf.push(
53598                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53599                     this.hdSelector, cid, " {\n", align, width, "}\n",
53600                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53601                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53602         }
53603         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53604     },
53605
53606     updateSplitters : function(){
53607         var cm = this.cm, s = this.getSplitters();
53608         if(s){ // splitters not created yet
53609             var pos = 0, locked = true;
53610             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53611                 if(cm.isHidden(i)) continue;
53612                 var w = cm.getColumnWidth(i); // make sure it's a number
53613                 if(!cm.isLocked(i) && locked){
53614                     pos = 0;
53615                     locked = false;
53616                 }
53617                 pos += w;
53618                 s[i].style.left = (pos-this.splitOffset) + "px";
53619             }
53620         }
53621     },
53622
53623     handleHiddenChange : function(colModel, colIndex, hidden){
53624         if(hidden){
53625             this.hideColumn(colIndex);
53626         }else{
53627             this.unhideColumn(colIndex);
53628         }
53629     },
53630
53631     hideColumn : function(colIndex){
53632         var cid = this.getColumnId(colIndex);
53633         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53634         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53635         if(Roo.isSafari){
53636             this.updateHeaders();
53637         }
53638         this.updateSplitters();
53639         this.layout();
53640     },
53641
53642     unhideColumn : function(colIndex){
53643         var cid = this.getColumnId(colIndex);
53644         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53645         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53646
53647         if(Roo.isSafari){
53648             this.updateHeaders();
53649         }
53650         this.updateSplitters();
53651         this.layout();
53652     },
53653
53654     insertRows : function(dm, firstRow, lastRow, isUpdate){
53655         if(firstRow == 0 && lastRow == dm.getCount()-1){
53656             this.refresh();
53657         }else{
53658             if(!isUpdate){
53659                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53660             }
53661             var s = this.getScrollState();
53662             var markup = this.renderRows(firstRow, lastRow);
53663             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53664             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53665             this.restoreScroll(s);
53666             if(!isUpdate){
53667                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53668                 this.syncRowHeights(firstRow, lastRow);
53669                 this.stripeRows(firstRow);
53670                 this.layout();
53671             }
53672         }
53673     },
53674
53675     bufferRows : function(markup, target, index){
53676         var before = null, trows = target.rows, tbody = target.tBodies[0];
53677         if(index < trows.length){
53678             before = trows[index];
53679         }
53680         var b = document.createElement("div");
53681         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53682         var rows = b.firstChild.rows;
53683         for(var i = 0, len = rows.length; i < len; i++){
53684             if(before){
53685                 tbody.insertBefore(rows[0], before);
53686             }else{
53687                 tbody.appendChild(rows[0]);
53688             }
53689         }
53690         b.innerHTML = "";
53691         b = null;
53692     },
53693
53694     deleteRows : function(dm, firstRow, lastRow){
53695         if(dm.getRowCount()<1){
53696             this.fireEvent("beforerefresh", this);
53697             this.mainBody.update("");
53698             this.lockedBody.update("");
53699             this.fireEvent("refresh", this);
53700         }else{
53701             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53702             var bt = this.getBodyTable();
53703             var tbody = bt.firstChild;
53704             var rows = bt.rows;
53705             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53706                 tbody.removeChild(rows[firstRow]);
53707             }
53708             this.stripeRows(firstRow);
53709             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53710         }
53711     },
53712
53713     updateRows : function(dataSource, firstRow, lastRow){
53714         var s = this.getScrollState();
53715         this.refresh();
53716         this.restoreScroll(s);
53717     },
53718
53719     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53720         if(!noRefresh){
53721            this.refresh();
53722         }
53723         this.updateHeaderSortState();
53724     },
53725
53726     getScrollState : function(){
53727         
53728         var sb = this.scroller.dom;
53729         return {left: sb.scrollLeft, top: sb.scrollTop};
53730     },
53731
53732     stripeRows : function(startRow){
53733         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53734             return;
53735         }
53736         startRow = startRow || 0;
53737         var rows = this.getBodyTable().rows;
53738         var lrows = this.getLockedTable().rows;
53739         var cls = ' x-grid-row-alt ';
53740         for(var i = startRow, len = rows.length; i < len; i++){
53741             var row = rows[i], lrow = lrows[i];
53742             var isAlt = ((i+1) % 2 == 0);
53743             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53744             if(isAlt == hasAlt){
53745                 continue;
53746             }
53747             if(isAlt){
53748                 row.className += " x-grid-row-alt";
53749             }else{
53750                 row.className = row.className.replace("x-grid-row-alt", "");
53751             }
53752             if(lrow){
53753                 lrow.className = row.className;
53754             }
53755         }
53756     },
53757
53758     restoreScroll : function(state){
53759         //Roo.log('GridView.restoreScroll');
53760         var sb = this.scroller.dom;
53761         sb.scrollLeft = state.left;
53762         sb.scrollTop = state.top;
53763         this.syncScroll();
53764     },
53765
53766     syncScroll : function(){
53767         //Roo.log('GridView.syncScroll');
53768         var sb = this.scroller.dom;
53769         var sh = this.mainHd.dom;
53770         var bs = this.mainBody.dom;
53771         var lv = this.lockedBody.dom;
53772         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53773         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53774     },
53775
53776     handleScroll : function(e){
53777         this.syncScroll();
53778         var sb = this.scroller.dom;
53779         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53780         e.stopEvent();
53781     },
53782
53783     handleWheel : function(e){
53784         var d = e.getWheelDelta();
53785         this.scroller.dom.scrollTop -= d*22;
53786         // set this here to prevent jumpy scrolling on large tables
53787         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53788         e.stopEvent();
53789     },
53790
53791     renderRows : function(startRow, endRow){
53792         // pull in all the crap needed to render rows
53793         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53794         var colCount = cm.getColumnCount();
53795
53796         if(ds.getCount() < 1){
53797             return ["", ""];
53798         }
53799
53800         // build a map for all the columns
53801         var cs = [];
53802         for(var i = 0; i < colCount; i++){
53803             var name = cm.getDataIndex(i);
53804             cs[i] = {
53805                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53806                 renderer : cm.getRenderer(i),
53807                 id : cm.getColumnId(i),
53808                 locked : cm.isLocked(i)
53809             };
53810         }
53811
53812         startRow = startRow || 0;
53813         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53814
53815         // records to render
53816         var rs = ds.getRange(startRow, endRow);
53817
53818         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53819     },
53820
53821     // As much as I hate to duplicate code, this was branched because FireFox really hates
53822     // [].join("") on strings. The performance difference was substantial enough to
53823     // branch this function
53824     doRender : Roo.isGecko ?
53825             function(cs, rs, ds, startRow, colCount, stripe){
53826                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53827                 // buffers
53828                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53829                 
53830                 var hasListener = this.grid.hasListener('rowclass');
53831                 var rowcfg = {};
53832                 for(var j = 0, len = rs.length; j < len; j++){
53833                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53834                     for(var i = 0; i < colCount; i++){
53835                         c = cs[i];
53836                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53837                         p.id = c.id;
53838                         p.css = p.attr = "";
53839                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53840                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53841                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53842                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53843                         }
53844                         var markup = ct.apply(p);
53845                         if(!c.locked){
53846                             cb+= markup;
53847                         }else{
53848                             lcb+= markup;
53849                         }
53850                     }
53851                     var alt = [];
53852                     if(stripe && ((rowIndex+1) % 2 == 0)){
53853                         alt.push("x-grid-row-alt")
53854                     }
53855                     if(r.dirty){
53856                         alt.push(  " x-grid-dirty-row");
53857                     }
53858                     rp.cells = lcb;
53859                     if(this.getRowClass){
53860                         alt.push(this.getRowClass(r, rowIndex));
53861                     }
53862                     if (hasListener) {
53863                         rowcfg = {
53864                              
53865                             record: r,
53866                             rowIndex : rowIndex,
53867                             rowClass : ''
53868                         }
53869                         this.grid.fireEvent('rowclass', this, rowcfg);
53870                         alt.push(rowcfg.rowClass);
53871                     }
53872                     rp.alt = alt.join(" ");
53873                     lbuf+= rt.apply(rp);
53874                     rp.cells = cb;
53875                     buf+=  rt.apply(rp);
53876                 }
53877                 return [lbuf, buf];
53878             } :
53879             function(cs, rs, ds, startRow, colCount, stripe){
53880                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53881                 // buffers
53882                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53883                 var hasListener = this.grid.hasListener('rowclass');
53884  
53885                 var rowcfg = {};
53886                 for(var j = 0, len = rs.length; j < len; j++){
53887                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53888                     for(var i = 0; i < colCount; i++){
53889                         c = cs[i];
53890                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53891                         p.id = c.id;
53892                         p.css = p.attr = "";
53893                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53894                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53895                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53896                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53897                         }
53898                         
53899                         var markup = ct.apply(p);
53900                         if(!c.locked){
53901                             cb[cb.length] = markup;
53902                         }else{
53903                             lcb[lcb.length] = markup;
53904                         }
53905                     }
53906                     var alt = [];
53907                     if(stripe && ((rowIndex+1) % 2 == 0)){
53908                         alt.push( "x-grid-row-alt");
53909                     }
53910                     if(r.dirty){
53911                         alt.push(" x-grid-dirty-row");
53912                     }
53913                     rp.cells = lcb;
53914                     if(this.getRowClass){
53915                         alt.push( this.getRowClass(r, rowIndex));
53916                     }
53917                     if (hasListener) {
53918                         rowcfg = {
53919                              
53920                             record: r,
53921                             rowIndex : rowIndex,
53922                             rowClass : ''
53923                         }
53924                         this.grid.fireEvent('rowclass', this, rowcfg);
53925                         alt.push(rowcfg.rowClass);
53926                     }
53927                     rp.alt = alt.join(" ");
53928                     rp.cells = lcb.join("");
53929                     lbuf[lbuf.length] = rt.apply(rp);
53930                     rp.cells = cb.join("");
53931                     buf[buf.length] =  rt.apply(rp);
53932                 }
53933                 return [lbuf.join(""), buf.join("")];
53934             },
53935
53936     renderBody : function(){
53937         var markup = this.renderRows();
53938         var bt = this.templates.body;
53939         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53940     },
53941
53942     /**
53943      * Refreshes the grid
53944      * @param {Boolean} headersToo
53945      */
53946     refresh : function(headersToo){
53947         this.fireEvent("beforerefresh", this);
53948         this.grid.stopEditing();
53949         var result = this.renderBody();
53950         this.lockedBody.update(result[0]);
53951         this.mainBody.update(result[1]);
53952         if(headersToo === true){
53953             this.updateHeaders();
53954             this.updateColumns();
53955             this.updateSplitters();
53956             this.updateHeaderSortState();
53957         }
53958         this.syncRowHeights();
53959         this.layout();
53960         this.fireEvent("refresh", this);
53961     },
53962
53963     handleColumnMove : function(cm, oldIndex, newIndex){
53964         this.indexMap = null;
53965         var s = this.getScrollState();
53966         this.refresh(true);
53967         this.restoreScroll(s);
53968         this.afterMove(newIndex);
53969     },
53970
53971     afterMove : function(colIndex){
53972         if(this.enableMoveAnim && Roo.enableFx){
53973             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53974         }
53975         // if multisort - fix sortOrder, and reload..
53976         if (this.grid.dataSource.multiSort) {
53977             // the we can call sort again..
53978             var dm = this.grid.dataSource;
53979             var cm = this.grid.colModel;
53980             var so = [];
53981             for(var i = 0; i < cm.config.length; i++ ) {
53982                 
53983                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53984                     continue; // dont' bother, it's not in sort list or being set.
53985                 }
53986                 
53987                 so.push(cm.config[i].dataIndex);
53988             };
53989             dm.sortOrder = so;
53990             dm.load(dm.lastOptions);
53991             
53992             
53993         }
53994         
53995     },
53996
53997     updateCell : function(dm, rowIndex, dataIndex){
53998         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53999         if(typeof colIndex == "undefined"){ // not present in grid
54000             return;
54001         }
54002         var cm = this.grid.colModel;
54003         var cell = this.getCell(rowIndex, colIndex);
54004         var cellText = this.getCellText(rowIndex, colIndex);
54005
54006         var p = {
54007             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54008             id : cm.getColumnId(colIndex),
54009             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54010         };
54011         var renderer = cm.getRenderer(colIndex);
54012         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54013         if(typeof val == "undefined" || val === "") val = "&#160;";
54014         cellText.innerHTML = val;
54015         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54016         this.syncRowHeights(rowIndex, rowIndex);
54017     },
54018
54019     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54020         var maxWidth = 0;
54021         if(this.grid.autoSizeHeaders){
54022             var h = this.getHeaderCellMeasure(colIndex);
54023             maxWidth = Math.max(maxWidth, h.scrollWidth);
54024         }
54025         var tb, index;
54026         if(this.cm.isLocked(colIndex)){
54027             tb = this.getLockedTable();
54028             index = colIndex;
54029         }else{
54030             tb = this.getBodyTable();
54031             index = colIndex - this.cm.getLockedCount();
54032         }
54033         if(tb && tb.rows){
54034             var rows = tb.rows;
54035             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54036             for(var i = 0; i < stopIndex; i++){
54037                 var cell = rows[i].childNodes[index].firstChild;
54038                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54039             }
54040         }
54041         return maxWidth + /*margin for error in IE*/ 5;
54042     },
54043     /**
54044      * Autofit a column to its content.
54045      * @param {Number} colIndex
54046      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54047      */
54048      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54049          if(this.cm.isHidden(colIndex)){
54050              return; // can't calc a hidden column
54051          }
54052         if(forceMinSize){
54053             var cid = this.cm.getColumnId(colIndex);
54054             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54055            if(this.grid.autoSizeHeaders){
54056                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54057            }
54058         }
54059         var newWidth = this.calcColumnWidth(colIndex);
54060         this.cm.setColumnWidth(colIndex,
54061             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54062         if(!suppressEvent){
54063             this.grid.fireEvent("columnresize", colIndex, newWidth);
54064         }
54065     },
54066
54067     /**
54068      * Autofits all columns to their content and then expands to fit any extra space in the grid
54069      */
54070      autoSizeColumns : function(){
54071         var cm = this.grid.colModel;
54072         var colCount = cm.getColumnCount();
54073         for(var i = 0; i < colCount; i++){
54074             this.autoSizeColumn(i, true, true);
54075         }
54076         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54077             this.fitColumns();
54078         }else{
54079             this.updateColumns();
54080             this.layout();
54081         }
54082     },
54083
54084     /**
54085      * Autofits all columns to the grid's width proportionate with their current size
54086      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54087      */
54088     fitColumns : function(reserveScrollSpace){
54089         var cm = this.grid.colModel;
54090         var colCount = cm.getColumnCount();
54091         var cols = [];
54092         var width = 0;
54093         var i, w;
54094         for (i = 0; i < colCount; i++){
54095             if(!cm.isHidden(i) && !cm.isFixed(i)){
54096                 w = cm.getColumnWidth(i);
54097                 cols.push(i);
54098                 cols.push(w);
54099                 width += w;
54100             }
54101         }
54102         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54103         if(reserveScrollSpace){
54104             avail -= 17;
54105         }
54106         var frac = (avail - cm.getTotalWidth())/width;
54107         while (cols.length){
54108             w = cols.pop();
54109             i = cols.pop();
54110             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54111         }
54112         this.updateColumns();
54113         this.layout();
54114     },
54115
54116     onRowSelect : function(rowIndex){
54117         var row = this.getRowComposite(rowIndex);
54118         row.addClass("x-grid-row-selected");
54119     },
54120
54121     onRowDeselect : function(rowIndex){
54122         var row = this.getRowComposite(rowIndex);
54123         row.removeClass("x-grid-row-selected");
54124     },
54125
54126     onCellSelect : function(row, col){
54127         var cell = this.getCell(row, col);
54128         if(cell){
54129             Roo.fly(cell).addClass("x-grid-cell-selected");
54130         }
54131     },
54132
54133     onCellDeselect : function(row, col){
54134         var cell = this.getCell(row, col);
54135         if(cell){
54136             Roo.fly(cell).removeClass("x-grid-cell-selected");
54137         }
54138     },
54139
54140     updateHeaderSortState : function(){
54141         
54142         // sort state can be single { field: xxx, direction : yyy}
54143         // or   { xxx=>ASC , yyy : DESC ..... }
54144         
54145         var mstate = {};
54146         if (!this.ds.multiSort) { 
54147             var state = this.ds.getSortState();
54148             if(!state){
54149                 return;
54150             }
54151             mstate[state.field] = state.direction;
54152             // FIXME... - this is not used here.. but might be elsewhere..
54153             this.sortState = state;
54154             
54155         } else {
54156             mstate = this.ds.sortToggle;
54157         }
54158         //remove existing sort classes..
54159         
54160         var sc = this.sortClasses;
54161         var hds = this.el.select(this.headerSelector).removeClass(sc);
54162         
54163         for(var f in mstate) {
54164         
54165             var sortColumn = this.cm.findColumnIndex(f);
54166             
54167             if(sortColumn != -1){
54168                 var sortDir = mstate[f];        
54169                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54170             }
54171         }
54172         
54173          
54174         
54175     },
54176
54177
54178     handleHeaderClick : function(g, index,e){
54179         
54180         Roo.log("header click");
54181         
54182         if (Roo.isTouch) {
54183             // touch events on header are handled by context
54184             this.handleHdCtx(g,index,e);
54185             return;
54186         }
54187         
54188         
54189         if(this.headersDisabled){
54190             return;
54191         }
54192         var dm = g.dataSource, cm = g.colModel;
54193         if(!cm.isSortable(index)){
54194             return;
54195         }
54196         g.stopEditing();
54197         
54198         if (dm.multiSort) {
54199             // update the sortOrder
54200             var so = [];
54201             for(var i = 0; i < cm.config.length; i++ ) {
54202                 
54203                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54204                     continue; // dont' bother, it's not in sort list or being set.
54205                 }
54206                 
54207                 so.push(cm.config[i].dataIndex);
54208             };
54209             dm.sortOrder = so;
54210         }
54211         
54212         
54213         dm.sort(cm.getDataIndex(index));
54214     },
54215
54216
54217     destroy : function(){
54218         if(this.colMenu){
54219             this.colMenu.removeAll();
54220             Roo.menu.MenuMgr.unregister(this.colMenu);
54221             this.colMenu.getEl().remove();
54222             delete this.colMenu;
54223         }
54224         if(this.hmenu){
54225             this.hmenu.removeAll();
54226             Roo.menu.MenuMgr.unregister(this.hmenu);
54227             this.hmenu.getEl().remove();
54228             delete this.hmenu;
54229         }
54230         if(this.grid.enableColumnMove){
54231             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54232             if(dds){
54233                 for(var dd in dds){
54234                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54235                         var elid = dds[dd].dragElId;
54236                         dds[dd].unreg();
54237                         Roo.get(elid).remove();
54238                     } else if(dds[dd].config.isTarget){
54239                         dds[dd].proxyTop.remove();
54240                         dds[dd].proxyBottom.remove();
54241                         dds[dd].unreg();
54242                     }
54243                     if(Roo.dd.DDM.locationCache[dd]){
54244                         delete Roo.dd.DDM.locationCache[dd];
54245                     }
54246                 }
54247                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54248             }
54249         }
54250         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54251         this.bind(null, null);
54252         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54253     },
54254
54255     handleLockChange : function(){
54256         this.refresh(true);
54257     },
54258
54259     onDenyColumnLock : function(){
54260
54261     },
54262
54263     onDenyColumnHide : function(){
54264
54265     },
54266
54267     handleHdMenuClick : function(item){
54268         var index = this.hdCtxIndex;
54269         var cm = this.cm, ds = this.ds;
54270         switch(item.id){
54271             case "asc":
54272                 ds.sort(cm.getDataIndex(index), "ASC");
54273                 break;
54274             case "desc":
54275                 ds.sort(cm.getDataIndex(index), "DESC");
54276                 break;
54277             case "lock":
54278                 var lc = cm.getLockedCount();
54279                 if(cm.getColumnCount(true) <= lc+1){
54280                     this.onDenyColumnLock();
54281                     return;
54282                 }
54283                 if(lc != index){
54284                     cm.setLocked(index, true, true);
54285                     cm.moveColumn(index, lc);
54286                     this.grid.fireEvent("columnmove", index, lc);
54287                 }else{
54288                     cm.setLocked(index, true);
54289                 }
54290             break;
54291             case "unlock":
54292                 var lc = cm.getLockedCount();
54293                 if((lc-1) != index){
54294                     cm.setLocked(index, false, true);
54295                     cm.moveColumn(index, lc-1);
54296                     this.grid.fireEvent("columnmove", index, lc-1);
54297                 }else{
54298                     cm.setLocked(index, false);
54299                 }
54300             break;
54301             case 'wider': // used to expand cols on touch..
54302             case 'narrow':
54303                 var cw = cm.getColumnWidth(index);
54304                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54305                 cw = Math.max(0, cw);
54306                 cw = Math.min(cw,4000);
54307                 cm.setColumnWidth(index, cw);
54308                 break;
54309                 
54310             default:
54311                 index = cm.getIndexById(item.id.substr(4));
54312                 if(index != -1){
54313                     if(item.checked && cm.getColumnCount(true) <= 1){
54314                         this.onDenyColumnHide();
54315                         return false;
54316                     }
54317                     cm.setHidden(index, item.checked);
54318                 }
54319         }
54320         return true;
54321     },
54322
54323     beforeColMenuShow : function(){
54324         var cm = this.cm,  colCount = cm.getColumnCount();
54325         this.colMenu.removeAll();
54326         for(var i = 0; i < colCount; i++){
54327             this.colMenu.add(new Roo.menu.CheckItem({
54328                 id: "col-"+cm.getColumnId(i),
54329                 text: cm.getColumnHeader(i),
54330                 checked: !cm.isHidden(i),
54331                 hideOnClick:false
54332             }));
54333         }
54334     },
54335
54336     handleHdCtx : function(g, index, e){
54337         e.stopEvent();
54338         var hd = this.getHeaderCell(index);
54339         this.hdCtxIndex = index;
54340         var ms = this.hmenu.items, cm = this.cm;
54341         ms.get("asc").setDisabled(!cm.isSortable(index));
54342         ms.get("desc").setDisabled(!cm.isSortable(index));
54343         if(this.grid.enableColLock !== false){
54344             ms.get("lock").setDisabled(cm.isLocked(index));
54345             ms.get("unlock").setDisabled(!cm.isLocked(index));
54346         }
54347         this.hmenu.show(hd, "tl-bl");
54348     },
54349
54350     handleHdOver : function(e){
54351         var hd = this.findHeaderCell(e.getTarget());
54352         if(hd && !this.headersDisabled){
54353             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54354                this.fly(hd).addClass("x-grid-hd-over");
54355             }
54356         }
54357     },
54358
54359     handleHdOut : function(e){
54360         var hd = this.findHeaderCell(e.getTarget());
54361         if(hd){
54362             this.fly(hd).removeClass("x-grid-hd-over");
54363         }
54364     },
54365
54366     handleSplitDblClick : function(e, t){
54367         var i = this.getCellIndex(t);
54368         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54369             this.autoSizeColumn(i, true);
54370             this.layout();
54371         }
54372     },
54373
54374     render : function(){
54375
54376         var cm = this.cm;
54377         var colCount = cm.getColumnCount();
54378
54379         if(this.grid.monitorWindowResize === true){
54380             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54381         }
54382         var header = this.renderHeaders();
54383         var body = this.templates.body.apply({rows:""});
54384         var html = this.templates.master.apply({
54385             lockedBody: body,
54386             body: body,
54387             lockedHeader: header[0],
54388             header: header[1]
54389         });
54390
54391         //this.updateColumns();
54392
54393         this.grid.getGridEl().dom.innerHTML = html;
54394
54395         this.initElements();
54396         
54397         // a kludge to fix the random scolling effect in webkit
54398         this.el.on("scroll", function() {
54399             this.el.dom.scrollTop=0; // hopefully not recursive..
54400         },this);
54401
54402         this.scroller.on("scroll", this.handleScroll, this);
54403         this.lockedBody.on("mousewheel", this.handleWheel, this);
54404         this.mainBody.on("mousewheel", this.handleWheel, this);
54405
54406         this.mainHd.on("mouseover", this.handleHdOver, this);
54407         this.mainHd.on("mouseout", this.handleHdOut, this);
54408         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54409                 {delegate: "."+this.splitClass});
54410
54411         this.lockedHd.on("mouseover", this.handleHdOver, this);
54412         this.lockedHd.on("mouseout", this.handleHdOut, this);
54413         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54414                 {delegate: "."+this.splitClass});
54415
54416         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54417             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54418         }
54419
54420         this.updateSplitters();
54421
54422         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54423             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54424             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54425         }
54426
54427         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54428             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54429             this.hmenu.add(
54430                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54431                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54432             );
54433             if(this.grid.enableColLock !== false){
54434                 this.hmenu.add('-',
54435                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54436                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54437                 );
54438             }
54439             if (Roo.isTouch) {
54440                  this.hmenu.add('-',
54441                     {id:"wider", text: this.columnsWiderText},
54442                     {id:"narrow", text: this.columnsNarrowText }
54443                 );
54444                 
54445                  
54446             }
54447             
54448             if(this.grid.enableColumnHide !== false){
54449
54450                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54451                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54452                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54453
54454                 this.hmenu.add('-',
54455                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54456                 );
54457             }
54458             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54459
54460             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54461         }
54462
54463         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54464             this.dd = new Roo.grid.GridDragZone(this.grid, {
54465                 ddGroup : this.grid.ddGroup || 'GridDD'
54466             });
54467             
54468         }
54469
54470         /*
54471         for(var i = 0; i < colCount; i++){
54472             if(cm.isHidden(i)){
54473                 this.hideColumn(i);
54474             }
54475             if(cm.config[i].align){
54476                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54477                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54478             }
54479         }*/
54480         
54481         this.updateHeaderSortState();
54482
54483         this.beforeInitialResize();
54484         this.layout(true);
54485
54486         // two part rendering gives faster view to the user
54487         this.renderPhase2.defer(1, this);
54488     },
54489
54490     renderPhase2 : function(){
54491         // render the rows now
54492         this.refresh();
54493         if(this.grid.autoSizeColumns){
54494             this.autoSizeColumns();
54495         }
54496     },
54497
54498     beforeInitialResize : function(){
54499
54500     },
54501
54502     onColumnSplitterMoved : function(i, w){
54503         this.userResized = true;
54504         var cm = this.grid.colModel;
54505         cm.setColumnWidth(i, w, true);
54506         var cid = cm.getColumnId(i);
54507         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54508         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54509         this.updateSplitters();
54510         this.layout();
54511         this.grid.fireEvent("columnresize", i, w);
54512     },
54513
54514     syncRowHeights : function(startIndex, endIndex){
54515         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54516             startIndex = startIndex || 0;
54517             var mrows = this.getBodyTable().rows;
54518             var lrows = this.getLockedTable().rows;
54519             var len = mrows.length-1;
54520             endIndex = Math.min(endIndex || len, len);
54521             for(var i = startIndex; i <= endIndex; i++){
54522                 var m = mrows[i], l = lrows[i];
54523                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54524                 m.style.height = l.style.height = h + "px";
54525             }
54526         }
54527     },
54528
54529     layout : function(initialRender, is2ndPass){
54530         var g = this.grid;
54531         var auto = g.autoHeight;
54532         var scrollOffset = 16;
54533         var c = g.getGridEl(), cm = this.cm,
54534                 expandCol = g.autoExpandColumn,
54535                 gv = this;
54536         //c.beginMeasure();
54537
54538         if(!c.dom.offsetWidth){ // display:none?
54539             if(initialRender){
54540                 this.lockedWrap.show();
54541                 this.mainWrap.show();
54542             }
54543             return;
54544         }
54545
54546         var hasLock = this.cm.isLocked(0);
54547
54548         var tbh = this.headerPanel.getHeight();
54549         var bbh = this.footerPanel.getHeight();
54550
54551         if(auto){
54552             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54553             var newHeight = ch + c.getBorderWidth("tb");
54554             if(g.maxHeight){
54555                 newHeight = Math.min(g.maxHeight, newHeight);
54556             }
54557             c.setHeight(newHeight);
54558         }
54559
54560         if(g.autoWidth){
54561             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54562         }
54563
54564         var s = this.scroller;
54565
54566         var csize = c.getSize(true);
54567
54568         this.el.setSize(csize.width, csize.height);
54569
54570         this.headerPanel.setWidth(csize.width);
54571         this.footerPanel.setWidth(csize.width);
54572
54573         var hdHeight = this.mainHd.getHeight();
54574         var vw = csize.width;
54575         var vh = csize.height - (tbh + bbh);
54576
54577         s.setSize(vw, vh);
54578
54579         var bt = this.getBodyTable();
54580         var ltWidth = hasLock ?
54581                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54582
54583         var scrollHeight = bt.offsetHeight;
54584         var scrollWidth = ltWidth + bt.offsetWidth;
54585         var vscroll = false, hscroll = false;
54586
54587         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54588
54589         var lw = this.lockedWrap, mw = this.mainWrap;
54590         var lb = this.lockedBody, mb = this.mainBody;
54591
54592         setTimeout(function(){
54593             var t = s.dom.offsetTop;
54594             var w = s.dom.clientWidth,
54595                 h = s.dom.clientHeight;
54596
54597             lw.setTop(t);
54598             lw.setSize(ltWidth, h);
54599
54600             mw.setLeftTop(ltWidth, t);
54601             mw.setSize(w-ltWidth, h);
54602
54603             lb.setHeight(h-hdHeight);
54604             mb.setHeight(h-hdHeight);
54605
54606             if(is2ndPass !== true && !gv.userResized && expandCol){
54607                 // high speed resize without full column calculation
54608                 
54609                 var ci = cm.getIndexById(expandCol);
54610                 if (ci < 0) {
54611                     ci = cm.findColumnIndex(expandCol);
54612                 }
54613                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54614                 var expandId = cm.getColumnId(ci);
54615                 var  tw = cm.getTotalWidth(false);
54616                 var currentWidth = cm.getColumnWidth(ci);
54617                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54618                 if(currentWidth != cw){
54619                     cm.setColumnWidth(ci, cw, true);
54620                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54621                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54622                     gv.updateSplitters();
54623                     gv.layout(false, true);
54624                 }
54625             }
54626
54627             if(initialRender){
54628                 lw.show();
54629                 mw.show();
54630             }
54631             //c.endMeasure();
54632         }, 10);
54633     },
54634
54635     onWindowResize : function(){
54636         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54637             return;
54638         }
54639         this.layout();
54640     },
54641
54642     appendFooter : function(parentEl){
54643         return null;
54644     },
54645
54646     sortAscText : "Sort Ascending",
54647     sortDescText : "Sort Descending",
54648     lockText : "Lock Column",
54649     unlockText : "Unlock Column",
54650     columnsText : "Columns",
54651  
54652     columnsWiderText : "Wider",
54653     columnsNarrowText : "Thinner"
54654 });
54655
54656
54657 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54658     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54659     this.proxy.el.addClass('x-grid3-col-dd');
54660 };
54661
54662 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54663     handleMouseDown : function(e){
54664
54665     },
54666
54667     callHandleMouseDown : function(e){
54668         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54669     }
54670 });
54671 /*
54672  * Based on:
54673  * Ext JS Library 1.1.1
54674  * Copyright(c) 2006-2007, Ext JS, LLC.
54675  *
54676  * Originally Released Under LGPL - original licence link has changed is not relivant.
54677  *
54678  * Fork - LGPL
54679  * <script type="text/javascript">
54680  */
54681  
54682 // private
54683 // This is a support class used internally by the Grid components
54684 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54685     this.grid = grid;
54686     this.view = grid.getView();
54687     this.proxy = this.view.resizeProxy;
54688     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54689         "gridSplitters" + this.grid.getGridEl().id, {
54690         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54691     });
54692     this.setHandleElId(Roo.id(hd));
54693     this.setOuterHandleElId(Roo.id(hd2));
54694     this.scroll = false;
54695 };
54696 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54697     fly: Roo.Element.fly,
54698
54699     b4StartDrag : function(x, y){
54700         this.view.headersDisabled = true;
54701         this.proxy.setHeight(this.view.mainWrap.getHeight());
54702         var w = this.cm.getColumnWidth(this.cellIndex);
54703         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54704         this.resetConstraints();
54705         this.setXConstraint(minw, 1000);
54706         this.setYConstraint(0, 0);
54707         this.minX = x - minw;
54708         this.maxX = x + 1000;
54709         this.startPos = x;
54710         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54711     },
54712
54713
54714     handleMouseDown : function(e){
54715         ev = Roo.EventObject.setEvent(e);
54716         var t = this.fly(ev.getTarget());
54717         if(t.hasClass("x-grid-split")){
54718             this.cellIndex = this.view.getCellIndex(t.dom);
54719             this.split = t.dom;
54720             this.cm = this.grid.colModel;
54721             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54722                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54723             }
54724         }
54725     },
54726
54727     endDrag : function(e){
54728         this.view.headersDisabled = false;
54729         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54730         var diff = endX - this.startPos;
54731         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54732     },
54733
54734     autoOffset : function(){
54735         this.setDelta(0,0);
54736     }
54737 });/*
54738  * Based on:
54739  * Ext JS Library 1.1.1
54740  * Copyright(c) 2006-2007, Ext JS, LLC.
54741  *
54742  * Originally Released Under LGPL - original licence link has changed is not relivant.
54743  *
54744  * Fork - LGPL
54745  * <script type="text/javascript">
54746  */
54747  
54748 // private
54749 // This is a support class used internally by the Grid components
54750 Roo.grid.GridDragZone = function(grid, config){
54751     this.view = grid.getView();
54752     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54753     if(this.view.lockedBody){
54754         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54755         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54756     }
54757     this.scroll = false;
54758     this.grid = grid;
54759     this.ddel = document.createElement('div');
54760     this.ddel.className = 'x-grid-dd-wrap';
54761 };
54762
54763 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54764     ddGroup : "GridDD",
54765
54766     getDragData : function(e){
54767         var t = Roo.lib.Event.getTarget(e);
54768         var rowIndex = this.view.findRowIndex(t);
54769         var sm = this.grid.selModel;
54770             
54771         //Roo.log(rowIndex);
54772         
54773         if (sm.getSelectedCell) {
54774             // cell selection..
54775             if (!sm.getSelectedCell()) {
54776                 return false;
54777             }
54778             if (rowIndex != sm.getSelectedCell()[0]) {
54779                 return false;
54780             }
54781         
54782         }
54783         
54784         if(rowIndex !== false){
54785             
54786             // if editorgrid.. 
54787             
54788             
54789             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54790                
54791             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54792               //  
54793             //}
54794             if (e.hasModifier()){
54795                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54796             }
54797             
54798             Roo.log("getDragData");
54799             
54800             return {
54801                 grid: this.grid,
54802                 ddel: this.ddel,
54803                 rowIndex: rowIndex,
54804                 selections:sm.getSelections ? sm.getSelections() : (
54805                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54806                 )
54807             };
54808         }
54809         return false;
54810     },
54811
54812     onInitDrag : function(e){
54813         var data = this.dragData;
54814         this.ddel.innerHTML = this.grid.getDragDropText();
54815         this.proxy.update(this.ddel);
54816         // fire start drag?
54817     },
54818
54819     afterRepair : function(){
54820         this.dragging = false;
54821     },
54822
54823     getRepairXY : function(e, data){
54824         return false;
54825     },
54826
54827     onEndDrag : function(data, e){
54828         // fire end drag?
54829     },
54830
54831     onValidDrop : function(dd, e, id){
54832         // fire drag drop?
54833         this.hideProxy();
54834     },
54835
54836     beforeInvalidDrop : function(e, id){
54837
54838     }
54839 });/*
54840  * Based on:
54841  * Ext JS Library 1.1.1
54842  * Copyright(c) 2006-2007, Ext JS, LLC.
54843  *
54844  * Originally Released Under LGPL - original licence link has changed is not relivant.
54845  *
54846  * Fork - LGPL
54847  * <script type="text/javascript">
54848  */
54849  
54850
54851 /**
54852  * @class Roo.grid.ColumnModel
54853  * @extends Roo.util.Observable
54854  * This is the default implementation of a ColumnModel used by the Grid. It defines
54855  * the columns in the grid.
54856  * <br>Usage:<br>
54857  <pre><code>
54858  var colModel = new Roo.grid.ColumnModel([
54859         {header: "Ticker", width: 60, sortable: true, locked: true},
54860         {header: "Company Name", width: 150, sortable: true},
54861         {header: "Market Cap.", width: 100, sortable: true},
54862         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54863         {header: "Employees", width: 100, sortable: true, resizable: false}
54864  ]);
54865  </code></pre>
54866  * <p>
54867  
54868  * The config options listed for this class are options which may appear in each
54869  * individual column definition.
54870  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54871  * @constructor
54872  * @param {Object} config An Array of column config objects. See this class's
54873  * config objects for details.
54874 */
54875 Roo.grid.ColumnModel = function(config){
54876         /**
54877      * The config passed into the constructor
54878      */
54879     this.config = config;
54880     this.lookup = {};
54881
54882     // if no id, create one
54883     // if the column does not have a dataIndex mapping,
54884     // map it to the order it is in the config
54885     for(var i = 0, len = config.length; i < len; i++){
54886         var c = config[i];
54887         if(typeof c.dataIndex == "undefined"){
54888             c.dataIndex = i;
54889         }
54890         if(typeof c.renderer == "string"){
54891             c.renderer = Roo.util.Format[c.renderer];
54892         }
54893         if(typeof c.id == "undefined"){
54894             c.id = Roo.id();
54895         }
54896         if(c.editor && c.editor.xtype){
54897             c.editor  = Roo.factory(c.editor, Roo.grid);
54898         }
54899         if(c.editor && c.editor.isFormField){
54900             c.editor = new Roo.grid.GridEditor(c.editor);
54901         }
54902         this.lookup[c.id] = c;
54903     }
54904
54905     /**
54906      * The width of columns which have no width specified (defaults to 100)
54907      * @type Number
54908      */
54909     this.defaultWidth = 100;
54910
54911     /**
54912      * Default sortable of columns which have no sortable specified (defaults to false)
54913      * @type Boolean
54914      */
54915     this.defaultSortable = false;
54916
54917     this.addEvents({
54918         /**
54919              * @event widthchange
54920              * Fires when the width of a column changes.
54921              * @param {ColumnModel} this
54922              * @param {Number} columnIndex The column index
54923              * @param {Number} newWidth The new width
54924              */
54925             "widthchange": true,
54926         /**
54927              * @event headerchange
54928              * Fires when the text of a header changes.
54929              * @param {ColumnModel} this
54930              * @param {Number} columnIndex The column index
54931              * @param {Number} newText The new header text
54932              */
54933             "headerchange": true,
54934         /**
54935              * @event hiddenchange
54936              * Fires when a column is hidden or "unhidden".
54937              * @param {ColumnModel} this
54938              * @param {Number} columnIndex The column index
54939              * @param {Boolean} hidden true if hidden, false otherwise
54940              */
54941             "hiddenchange": true,
54942             /**
54943          * @event columnmoved
54944          * Fires when a column is moved.
54945          * @param {ColumnModel} this
54946          * @param {Number} oldIndex
54947          * @param {Number} newIndex
54948          */
54949         "columnmoved" : true,
54950         /**
54951          * @event columlockchange
54952          * Fires when a column's locked state is changed
54953          * @param {ColumnModel} this
54954          * @param {Number} colIndex
54955          * @param {Boolean} locked true if locked
54956          */
54957         "columnlockchange" : true
54958     });
54959     Roo.grid.ColumnModel.superclass.constructor.call(this);
54960 };
54961 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54962     /**
54963      * @cfg {String} header The header text to display in the Grid view.
54964      */
54965     /**
54966      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54967      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54968      * specified, the column's index is used as an index into the Record's data Array.
54969      */
54970     /**
54971      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54972      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54973      */
54974     /**
54975      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54976      * Defaults to the value of the {@link #defaultSortable} property.
54977      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54978      */
54979     /**
54980      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54981      */
54982     /**
54983      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54984      */
54985     /**
54986      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54987      */
54988     /**
54989      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54990      */
54991     /**
54992      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54993      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54994      * default renderer uses the raw data value. If an object is returned (bootstrap only)
54995      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
54996      */
54997        /**
54998      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54999      */
55000     /**
55001      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55002      */
55003
55004     /**
55005      * Returns the id of the column at the specified index.
55006      * @param {Number} index The column index
55007      * @return {String} the id
55008      */
55009     getColumnId : function(index){
55010         return this.config[index].id;
55011     },
55012
55013     /**
55014      * Returns the column for a specified id.
55015      * @param {String} id The column id
55016      * @return {Object} the column
55017      */
55018     getColumnById : function(id){
55019         return this.lookup[id];
55020     },
55021
55022     
55023     /**
55024      * Returns the column for a specified dataIndex.
55025      * @param {String} dataIndex The column dataIndex
55026      * @return {Object|Boolean} the column or false if not found
55027      */
55028     getColumnByDataIndex: function(dataIndex){
55029         var index = this.findColumnIndex(dataIndex);
55030         return index > -1 ? this.config[index] : false;
55031     },
55032     
55033     /**
55034      * Returns the index for a specified column id.
55035      * @param {String} id The column id
55036      * @return {Number} the index, or -1 if not found
55037      */
55038     getIndexById : function(id){
55039         for(var i = 0, len = this.config.length; i < len; i++){
55040             if(this.config[i].id == id){
55041                 return i;
55042             }
55043         }
55044         return -1;
55045     },
55046     
55047     /**
55048      * Returns the index for a specified column dataIndex.
55049      * @param {String} dataIndex The column dataIndex
55050      * @return {Number} the index, or -1 if not found
55051      */
55052     
55053     findColumnIndex : function(dataIndex){
55054         for(var i = 0, len = this.config.length; i < len; i++){
55055             if(this.config[i].dataIndex == dataIndex){
55056                 return i;
55057             }
55058         }
55059         return -1;
55060     },
55061     
55062     
55063     moveColumn : function(oldIndex, newIndex){
55064         var c = this.config[oldIndex];
55065         this.config.splice(oldIndex, 1);
55066         this.config.splice(newIndex, 0, c);
55067         this.dataMap = null;
55068         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55069     },
55070
55071     isLocked : function(colIndex){
55072         return this.config[colIndex].locked === true;
55073     },
55074
55075     setLocked : function(colIndex, value, suppressEvent){
55076         if(this.isLocked(colIndex) == value){
55077             return;
55078         }
55079         this.config[colIndex].locked = value;
55080         if(!suppressEvent){
55081             this.fireEvent("columnlockchange", this, colIndex, value);
55082         }
55083     },
55084
55085     getTotalLockedWidth : function(){
55086         var totalWidth = 0;
55087         for(var i = 0; i < this.config.length; i++){
55088             if(this.isLocked(i) && !this.isHidden(i)){
55089                 this.totalWidth += this.getColumnWidth(i);
55090             }
55091         }
55092         return totalWidth;
55093     },
55094
55095     getLockedCount : function(){
55096         for(var i = 0, len = this.config.length; i < len; i++){
55097             if(!this.isLocked(i)){
55098                 return i;
55099             }
55100         }
55101     },
55102
55103     /**
55104      * Returns the number of columns.
55105      * @return {Number}
55106      */
55107     getColumnCount : function(visibleOnly){
55108         if(visibleOnly === true){
55109             var c = 0;
55110             for(var i = 0, len = this.config.length; i < len; i++){
55111                 if(!this.isHidden(i)){
55112                     c++;
55113                 }
55114             }
55115             return c;
55116         }
55117         return this.config.length;
55118     },
55119
55120     /**
55121      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55122      * @param {Function} fn
55123      * @param {Object} scope (optional)
55124      * @return {Array} result
55125      */
55126     getColumnsBy : function(fn, scope){
55127         var r = [];
55128         for(var i = 0, len = this.config.length; i < len; i++){
55129             var c = this.config[i];
55130             if(fn.call(scope||this, c, i) === true){
55131                 r[r.length] = c;
55132             }
55133         }
55134         return r;
55135     },
55136
55137     /**
55138      * Returns true if the specified column is sortable.
55139      * @param {Number} col The column index
55140      * @return {Boolean}
55141      */
55142     isSortable : function(col){
55143         if(typeof this.config[col].sortable == "undefined"){
55144             return this.defaultSortable;
55145         }
55146         return this.config[col].sortable;
55147     },
55148
55149     /**
55150      * Returns the rendering (formatting) function defined for the column.
55151      * @param {Number} col The column index.
55152      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55153      */
55154     getRenderer : function(col){
55155         if(!this.config[col].renderer){
55156             return Roo.grid.ColumnModel.defaultRenderer;
55157         }
55158         return this.config[col].renderer;
55159     },
55160
55161     /**
55162      * Sets the rendering (formatting) function for a column.
55163      * @param {Number} col The column index
55164      * @param {Function} fn The function to use to process the cell's raw data
55165      * to return HTML markup for the grid view. The render function is called with
55166      * the following parameters:<ul>
55167      * <li>Data value.</li>
55168      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55169      * <li>css A CSS style string to apply to the table cell.</li>
55170      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55171      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55172      * <li>Row index</li>
55173      * <li>Column index</li>
55174      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55175      */
55176     setRenderer : function(col, fn){
55177         this.config[col].renderer = fn;
55178     },
55179
55180     /**
55181      * Returns the width for the specified column.
55182      * @param {Number} col The column index
55183      * @return {Number}
55184      */
55185     getColumnWidth : function(col){
55186         return this.config[col].width * 1 || this.defaultWidth;
55187     },
55188
55189     /**
55190      * Sets the width for a column.
55191      * @param {Number} col The column index
55192      * @param {Number} width The new width
55193      */
55194     setColumnWidth : function(col, width, suppressEvent){
55195         this.config[col].width = width;
55196         this.totalWidth = null;
55197         if(!suppressEvent){
55198              this.fireEvent("widthchange", this, col, width);
55199         }
55200     },
55201
55202     /**
55203      * Returns the total width of all columns.
55204      * @param {Boolean} includeHidden True to include hidden column widths
55205      * @return {Number}
55206      */
55207     getTotalWidth : function(includeHidden){
55208         if(!this.totalWidth){
55209             this.totalWidth = 0;
55210             for(var i = 0, len = this.config.length; i < len; i++){
55211                 if(includeHidden || !this.isHidden(i)){
55212                     this.totalWidth += this.getColumnWidth(i);
55213                 }
55214             }
55215         }
55216         return this.totalWidth;
55217     },
55218
55219     /**
55220      * Returns the header for the specified column.
55221      * @param {Number} col The column index
55222      * @return {String}
55223      */
55224     getColumnHeader : function(col){
55225         return this.config[col].header;
55226     },
55227
55228     /**
55229      * Sets the header for a column.
55230      * @param {Number} col The column index
55231      * @param {String} header The new header
55232      */
55233     setColumnHeader : function(col, header){
55234         this.config[col].header = header;
55235         this.fireEvent("headerchange", this, col, header);
55236     },
55237
55238     /**
55239      * Returns the tooltip for the specified column.
55240      * @param {Number} col The column index
55241      * @return {String}
55242      */
55243     getColumnTooltip : function(col){
55244             return this.config[col].tooltip;
55245     },
55246     /**
55247      * Sets the tooltip for a column.
55248      * @param {Number} col The column index
55249      * @param {String} tooltip The new tooltip
55250      */
55251     setColumnTooltip : function(col, tooltip){
55252             this.config[col].tooltip = tooltip;
55253     },
55254
55255     /**
55256      * Returns the dataIndex for the specified column.
55257      * @param {Number} col The column index
55258      * @return {Number}
55259      */
55260     getDataIndex : function(col){
55261         return this.config[col].dataIndex;
55262     },
55263
55264     /**
55265      * Sets the dataIndex for a column.
55266      * @param {Number} col The column index
55267      * @param {Number} dataIndex The new dataIndex
55268      */
55269     setDataIndex : function(col, dataIndex){
55270         this.config[col].dataIndex = dataIndex;
55271     },
55272
55273     
55274     
55275     /**
55276      * Returns true if the cell is editable.
55277      * @param {Number} colIndex The column index
55278      * @param {Number} rowIndex The row index
55279      * @return {Boolean}
55280      */
55281     isCellEditable : function(colIndex, rowIndex){
55282         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55283     },
55284
55285     /**
55286      * Returns the editor defined for the cell/column.
55287      * return false or null to disable editing.
55288      * @param {Number} colIndex The column index
55289      * @param {Number} rowIndex The row index
55290      * @return {Object}
55291      */
55292     getCellEditor : function(colIndex, rowIndex){
55293         return this.config[colIndex].editor;
55294     },
55295
55296     /**
55297      * Sets if a column is editable.
55298      * @param {Number} col The column index
55299      * @param {Boolean} editable True if the column is editable
55300      */
55301     setEditable : function(col, editable){
55302         this.config[col].editable = editable;
55303     },
55304
55305
55306     /**
55307      * Returns true if the column is hidden.
55308      * @param {Number} colIndex The column index
55309      * @return {Boolean}
55310      */
55311     isHidden : function(colIndex){
55312         return this.config[colIndex].hidden;
55313     },
55314
55315
55316     /**
55317      * Returns true if the column width cannot be changed
55318      */
55319     isFixed : function(colIndex){
55320         return this.config[colIndex].fixed;
55321     },
55322
55323     /**
55324      * Returns true if the column can be resized
55325      * @return {Boolean}
55326      */
55327     isResizable : function(colIndex){
55328         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55329     },
55330     /**
55331      * Sets if a column is hidden.
55332      * @param {Number} colIndex The column index
55333      * @param {Boolean} hidden True if the column is hidden
55334      */
55335     setHidden : function(colIndex, hidden){
55336         this.config[colIndex].hidden = hidden;
55337         this.totalWidth = null;
55338         this.fireEvent("hiddenchange", this, colIndex, hidden);
55339     },
55340
55341     /**
55342      * Sets the editor for a column.
55343      * @param {Number} col The column index
55344      * @param {Object} editor The editor object
55345      */
55346     setEditor : function(col, editor){
55347         this.config[col].editor = editor;
55348     }
55349 });
55350
55351 Roo.grid.ColumnModel.defaultRenderer = function(value){
55352         if(typeof value == "string" && value.length < 1){
55353             return "&#160;";
55354         }
55355         return value;
55356 };
55357
55358 // Alias for backwards compatibility
55359 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55360 /*
55361  * Based on:
55362  * Ext JS Library 1.1.1
55363  * Copyright(c) 2006-2007, Ext JS, LLC.
55364  *
55365  * Originally Released Under LGPL - original licence link has changed is not relivant.
55366  *
55367  * Fork - LGPL
55368  * <script type="text/javascript">
55369  */
55370
55371 /**
55372  * @class Roo.grid.AbstractSelectionModel
55373  * @extends Roo.util.Observable
55374  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55375  * implemented by descendant classes.  This class should not be directly instantiated.
55376  * @constructor
55377  */
55378 Roo.grid.AbstractSelectionModel = function(){
55379     this.locked = false;
55380     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55381 };
55382
55383 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55384     /** @ignore Called by the grid automatically. Do not call directly. */
55385     init : function(grid){
55386         this.grid = grid;
55387         this.initEvents();
55388     },
55389
55390     /**
55391      * Locks the selections.
55392      */
55393     lock : function(){
55394         this.locked = true;
55395     },
55396
55397     /**
55398      * Unlocks the selections.
55399      */
55400     unlock : function(){
55401         this.locked = false;
55402     },
55403
55404     /**
55405      * Returns true if the selections are locked.
55406      * @return {Boolean}
55407      */
55408     isLocked : function(){
55409         return this.locked;
55410     }
55411 });/*
55412  * Based on:
55413  * Ext JS Library 1.1.1
55414  * Copyright(c) 2006-2007, Ext JS, LLC.
55415  *
55416  * Originally Released Under LGPL - original licence link has changed is not relivant.
55417  *
55418  * Fork - LGPL
55419  * <script type="text/javascript">
55420  */
55421 /**
55422  * @extends Roo.grid.AbstractSelectionModel
55423  * @class Roo.grid.RowSelectionModel
55424  * The default SelectionModel used by {@link Roo.grid.Grid}.
55425  * It supports multiple selections and keyboard selection/navigation. 
55426  * @constructor
55427  * @param {Object} config
55428  */
55429 Roo.grid.RowSelectionModel = function(config){
55430     Roo.apply(this, config);
55431     this.selections = new Roo.util.MixedCollection(false, function(o){
55432         return o.id;
55433     });
55434
55435     this.last = false;
55436     this.lastActive = false;
55437
55438     this.addEvents({
55439         /**
55440              * @event selectionchange
55441              * Fires when the selection changes
55442              * @param {SelectionModel} this
55443              */
55444             "selectionchange" : true,
55445         /**
55446              * @event afterselectionchange
55447              * Fires after the selection changes (eg. by key press or clicking)
55448              * @param {SelectionModel} this
55449              */
55450             "afterselectionchange" : true,
55451         /**
55452              * @event beforerowselect
55453              * Fires when a row is selected being selected, return false to cancel.
55454              * @param {SelectionModel} this
55455              * @param {Number} rowIndex The selected index
55456              * @param {Boolean} keepExisting False if other selections will be cleared
55457              */
55458             "beforerowselect" : true,
55459         /**
55460              * @event rowselect
55461              * Fires when a row is selected.
55462              * @param {SelectionModel} this
55463              * @param {Number} rowIndex The selected index
55464              * @param {Roo.data.Record} r The record
55465              */
55466             "rowselect" : true,
55467         /**
55468              * @event rowdeselect
55469              * Fires when a row is deselected.
55470              * @param {SelectionModel} this
55471              * @param {Number} rowIndex The selected index
55472              */
55473         "rowdeselect" : true
55474     });
55475     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55476     this.locked = false;
55477 };
55478
55479 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55480     /**
55481      * @cfg {Boolean} singleSelect
55482      * True to allow selection of only one row at a time (defaults to false)
55483      */
55484     singleSelect : false,
55485
55486     // private
55487     initEvents : function(){
55488
55489         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55490             this.grid.on("mousedown", this.handleMouseDown, this);
55491         }else{ // allow click to work like normal
55492             this.grid.on("rowclick", this.handleDragableRowClick, this);
55493         }
55494
55495         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55496             "up" : function(e){
55497                 if(!e.shiftKey){
55498                     this.selectPrevious(e.shiftKey);
55499                 }else if(this.last !== false && this.lastActive !== false){
55500                     var last = this.last;
55501                     this.selectRange(this.last,  this.lastActive-1);
55502                     this.grid.getView().focusRow(this.lastActive);
55503                     if(last !== false){
55504                         this.last = last;
55505                     }
55506                 }else{
55507                     this.selectFirstRow();
55508                 }
55509                 this.fireEvent("afterselectionchange", this);
55510             },
55511             "down" : function(e){
55512                 if(!e.shiftKey){
55513                     this.selectNext(e.shiftKey);
55514                 }else if(this.last !== false && this.lastActive !== false){
55515                     var last = this.last;
55516                     this.selectRange(this.last,  this.lastActive+1);
55517                     this.grid.getView().focusRow(this.lastActive);
55518                     if(last !== false){
55519                         this.last = last;
55520                     }
55521                 }else{
55522                     this.selectFirstRow();
55523                 }
55524                 this.fireEvent("afterselectionchange", this);
55525             },
55526             scope: this
55527         });
55528
55529         var view = this.grid.view;
55530         view.on("refresh", this.onRefresh, this);
55531         view.on("rowupdated", this.onRowUpdated, this);
55532         view.on("rowremoved", this.onRemove, this);
55533     },
55534
55535     // private
55536     onRefresh : function(){
55537         var ds = this.grid.dataSource, i, v = this.grid.view;
55538         var s = this.selections;
55539         s.each(function(r){
55540             if((i = ds.indexOfId(r.id)) != -1){
55541                 v.onRowSelect(i);
55542             }else{
55543                 s.remove(r);
55544             }
55545         });
55546     },
55547
55548     // private
55549     onRemove : function(v, index, r){
55550         this.selections.remove(r);
55551     },
55552
55553     // private
55554     onRowUpdated : function(v, index, r){
55555         if(this.isSelected(r)){
55556             v.onRowSelect(index);
55557         }
55558     },
55559
55560     /**
55561      * Select records.
55562      * @param {Array} records The records to select
55563      * @param {Boolean} keepExisting (optional) True to keep existing selections
55564      */
55565     selectRecords : function(records, keepExisting){
55566         if(!keepExisting){
55567             this.clearSelections();
55568         }
55569         var ds = this.grid.dataSource;
55570         for(var i = 0, len = records.length; i < len; i++){
55571             this.selectRow(ds.indexOf(records[i]), true);
55572         }
55573     },
55574
55575     /**
55576      * Gets the number of selected rows.
55577      * @return {Number}
55578      */
55579     getCount : function(){
55580         return this.selections.length;
55581     },
55582
55583     /**
55584      * Selects the first row in the grid.
55585      */
55586     selectFirstRow : function(){
55587         this.selectRow(0);
55588     },
55589
55590     /**
55591      * Select the last row.
55592      * @param {Boolean} keepExisting (optional) True to keep existing selections
55593      */
55594     selectLastRow : function(keepExisting){
55595         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55596     },
55597
55598     /**
55599      * Selects the row immediately following the last selected row.
55600      * @param {Boolean} keepExisting (optional) True to keep existing selections
55601      */
55602     selectNext : function(keepExisting){
55603         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55604             this.selectRow(this.last+1, keepExisting);
55605             this.grid.getView().focusRow(this.last);
55606         }
55607     },
55608
55609     /**
55610      * Selects the row that precedes the last selected row.
55611      * @param {Boolean} keepExisting (optional) True to keep existing selections
55612      */
55613     selectPrevious : function(keepExisting){
55614         if(this.last){
55615             this.selectRow(this.last-1, keepExisting);
55616             this.grid.getView().focusRow(this.last);
55617         }
55618     },
55619
55620     /**
55621      * Returns the selected records
55622      * @return {Array} Array of selected records
55623      */
55624     getSelections : function(){
55625         return [].concat(this.selections.items);
55626     },
55627
55628     /**
55629      * Returns the first selected record.
55630      * @return {Record}
55631      */
55632     getSelected : function(){
55633         return this.selections.itemAt(0);
55634     },
55635
55636
55637     /**
55638      * Clears all selections.
55639      */
55640     clearSelections : function(fast){
55641         if(this.locked) return;
55642         if(fast !== true){
55643             var ds = this.grid.dataSource;
55644             var s = this.selections;
55645             s.each(function(r){
55646                 this.deselectRow(ds.indexOfId(r.id));
55647             }, this);
55648             s.clear();
55649         }else{
55650             this.selections.clear();
55651         }
55652         this.last = false;
55653     },
55654
55655
55656     /**
55657      * Selects all rows.
55658      */
55659     selectAll : function(){
55660         if(this.locked) return;
55661         this.selections.clear();
55662         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55663             this.selectRow(i, true);
55664         }
55665     },
55666
55667     /**
55668      * Returns True if there is a selection.
55669      * @return {Boolean}
55670      */
55671     hasSelection : function(){
55672         return this.selections.length > 0;
55673     },
55674
55675     /**
55676      * Returns True if the specified row is selected.
55677      * @param {Number/Record} record The record or index of the record to check
55678      * @return {Boolean}
55679      */
55680     isSelected : function(index){
55681         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55682         return (r && this.selections.key(r.id) ? true : false);
55683     },
55684
55685     /**
55686      * Returns True if the specified record id is selected.
55687      * @param {String} id The id of record to check
55688      * @return {Boolean}
55689      */
55690     isIdSelected : function(id){
55691         return (this.selections.key(id) ? true : false);
55692     },
55693
55694     // private
55695     handleMouseDown : function(e, t){
55696         var view = this.grid.getView(), rowIndex;
55697         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55698             return;
55699         };
55700         if(e.shiftKey && this.last !== false){
55701             var last = this.last;
55702             this.selectRange(last, rowIndex, e.ctrlKey);
55703             this.last = last; // reset the last
55704             view.focusRow(rowIndex);
55705         }else{
55706             var isSelected = this.isSelected(rowIndex);
55707             if(e.button !== 0 && isSelected){
55708                 view.focusRow(rowIndex);
55709             }else if(e.ctrlKey && isSelected){
55710                 this.deselectRow(rowIndex);
55711             }else if(!isSelected){
55712                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55713                 view.focusRow(rowIndex);
55714             }
55715         }
55716         this.fireEvent("afterselectionchange", this);
55717     },
55718     // private
55719     handleDragableRowClick :  function(grid, rowIndex, e) 
55720     {
55721         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55722             this.selectRow(rowIndex, false);
55723             grid.view.focusRow(rowIndex);
55724              this.fireEvent("afterselectionchange", this);
55725         }
55726     },
55727     
55728     /**
55729      * Selects multiple rows.
55730      * @param {Array} rows Array of the indexes of the row to select
55731      * @param {Boolean} keepExisting (optional) True to keep existing selections
55732      */
55733     selectRows : function(rows, keepExisting){
55734         if(!keepExisting){
55735             this.clearSelections();
55736         }
55737         for(var i = 0, len = rows.length; i < len; i++){
55738             this.selectRow(rows[i], true);
55739         }
55740     },
55741
55742     /**
55743      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55744      * @param {Number} startRow The index of the first row in the range
55745      * @param {Number} endRow The index of the last row in the range
55746      * @param {Boolean} keepExisting (optional) True to retain existing selections
55747      */
55748     selectRange : function(startRow, endRow, keepExisting){
55749         if(this.locked) return;
55750         if(!keepExisting){
55751             this.clearSelections();
55752         }
55753         if(startRow <= endRow){
55754             for(var i = startRow; i <= endRow; i++){
55755                 this.selectRow(i, true);
55756             }
55757         }else{
55758             for(var i = startRow; i >= endRow; i--){
55759                 this.selectRow(i, true);
55760             }
55761         }
55762     },
55763
55764     /**
55765      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55766      * @param {Number} startRow The index of the first row in the range
55767      * @param {Number} endRow The index of the last row in the range
55768      */
55769     deselectRange : function(startRow, endRow, preventViewNotify){
55770         if(this.locked) return;
55771         for(var i = startRow; i <= endRow; i++){
55772             this.deselectRow(i, preventViewNotify);
55773         }
55774     },
55775
55776     /**
55777      * Selects a row.
55778      * @param {Number} row The index of the row to select
55779      * @param {Boolean} keepExisting (optional) True to keep existing selections
55780      */
55781     selectRow : function(index, keepExisting, preventViewNotify){
55782         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55783         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55784             if(!keepExisting || this.singleSelect){
55785                 this.clearSelections();
55786             }
55787             var r = this.grid.dataSource.getAt(index);
55788             this.selections.add(r);
55789             this.last = this.lastActive = index;
55790             if(!preventViewNotify){
55791                 this.grid.getView().onRowSelect(index);
55792             }
55793             this.fireEvent("rowselect", this, index, r);
55794             this.fireEvent("selectionchange", this);
55795         }
55796     },
55797
55798     /**
55799      * Deselects a row.
55800      * @param {Number} row The index of the row to deselect
55801      */
55802     deselectRow : function(index, preventViewNotify){
55803         if(this.locked) return;
55804         if(this.last == index){
55805             this.last = false;
55806         }
55807         if(this.lastActive == index){
55808             this.lastActive = false;
55809         }
55810         var r = this.grid.dataSource.getAt(index);
55811         this.selections.remove(r);
55812         if(!preventViewNotify){
55813             this.grid.getView().onRowDeselect(index);
55814         }
55815         this.fireEvent("rowdeselect", this, index);
55816         this.fireEvent("selectionchange", this);
55817     },
55818
55819     // private
55820     restoreLast : function(){
55821         if(this._last){
55822             this.last = this._last;
55823         }
55824     },
55825
55826     // private
55827     acceptsNav : function(row, col, cm){
55828         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55829     },
55830
55831     // private
55832     onEditorKey : function(field, e){
55833         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55834         if(k == e.TAB){
55835             e.stopEvent();
55836             ed.completeEdit();
55837             if(e.shiftKey){
55838                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55839             }else{
55840                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55841             }
55842         }else if(k == e.ENTER && !e.ctrlKey){
55843             e.stopEvent();
55844             ed.completeEdit();
55845             if(e.shiftKey){
55846                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55847             }else{
55848                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55849             }
55850         }else if(k == e.ESC){
55851             ed.cancelEdit();
55852         }
55853         if(newCell){
55854             g.startEditing(newCell[0], newCell[1]);
55855         }
55856     }
55857 });/*
55858  * Based on:
55859  * Ext JS Library 1.1.1
55860  * Copyright(c) 2006-2007, Ext JS, LLC.
55861  *
55862  * Originally Released Under LGPL - original licence link has changed is not relivant.
55863  *
55864  * Fork - LGPL
55865  * <script type="text/javascript">
55866  */
55867 /**
55868  * @class Roo.grid.CellSelectionModel
55869  * @extends Roo.grid.AbstractSelectionModel
55870  * This class provides the basic implementation for cell selection in a grid.
55871  * @constructor
55872  * @param {Object} config The object containing the configuration of this model.
55873  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55874  */
55875 Roo.grid.CellSelectionModel = function(config){
55876     Roo.apply(this, config);
55877
55878     this.selection = null;
55879
55880     this.addEvents({
55881         /**
55882              * @event beforerowselect
55883              * Fires before a cell is selected.
55884              * @param {SelectionModel} this
55885              * @param {Number} rowIndex The selected row index
55886              * @param {Number} colIndex The selected cell index
55887              */
55888             "beforecellselect" : true,
55889         /**
55890              * @event cellselect
55891              * Fires when a cell is selected.
55892              * @param {SelectionModel} this
55893              * @param {Number} rowIndex The selected row index
55894              * @param {Number} colIndex The selected cell index
55895              */
55896             "cellselect" : true,
55897         /**
55898              * @event selectionchange
55899              * Fires when the active selection changes.
55900              * @param {SelectionModel} this
55901              * @param {Object} selection null for no selection or an object (o) with two properties
55902                 <ul>
55903                 <li>o.record: the record object for the row the selection is in</li>
55904                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55905                 </ul>
55906              */
55907             "selectionchange" : true,
55908         /**
55909              * @event tabend
55910              * Fires when the tab (or enter) was pressed on the last editable cell
55911              * You can use this to trigger add new row.
55912              * @param {SelectionModel} this
55913              */
55914             "tabend" : true,
55915          /**
55916              * @event beforeeditnext
55917              * Fires before the next editable sell is made active
55918              * You can use this to skip to another cell or fire the tabend
55919              *    if you set cell to false
55920              * @param {Object} eventdata object : { cell : [ row, col ] } 
55921              */
55922             "beforeeditnext" : true
55923     });
55924     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55925 };
55926
55927 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55928     
55929     enter_is_tab: false,
55930
55931     /** @ignore */
55932     initEvents : function(){
55933         this.grid.on("mousedown", this.handleMouseDown, this);
55934         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55935         var view = this.grid.view;
55936         view.on("refresh", this.onViewChange, this);
55937         view.on("rowupdated", this.onRowUpdated, this);
55938         view.on("beforerowremoved", this.clearSelections, this);
55939         view.on("beforerowsinserted", this.clearSelections, this);
55940         if(this.grid.isEditor){
55941             this.grid.on("beforeedit", this.beforeEdit,  this);
55942         }
55943     },
55944
55945         //private
55946     beforeEdit : function(e){
55947         this.select(e.row, e.column, false, true, e.record);
55948     },
55949
55950         //private
55951     onRowUpdated : function(v, index, r){
55952         if(this.selection && this.selection.record == r){
55953             v.onCellSelect(index, this.selection.cell[1]);
55954         }
55955     },
55956
55957         //private
55958     onViewChange : function(){
55959         this.clearSelections(true);
55960     },
55961
55962         /**
55963          * Returns the currently selected cell,.
55964          * @return {Array} The selected cell (row, column) or null if none selected.
55965          */
55966     getSelectedCell : function(){
55967         return this.selection ? this.selection.cell : null;
55968     },
55969
55970     /**
55971      * Clears all selections.
55972      * @param {Boolean} true to prevent the gridview from being notified about the change.
55973      */
55974     clearSelections : function(preventNotify){
55975         var s = this.selection;
55976         if(s){
55977             if(preventNotify !== true){
55978                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55979             }
55980             this.selection = null;
55981             this.fireEvent("selectionchange", this, null);
55982         }
55983     },
55984
55985     /**
55986      * Returns true if there is a selection.
55987      * @return {Boolean}
55988      */
55989     hasSelection : function(){
55990         return this.selection ? true : false;
55991     },
55992
55993     /** @ignore */
55994     handleMouseDown : function(e, t){
55995         var v = this.grid.getView();
55996         if(this.isLocked()){
55997             return;
55998         };
55999         var row = v.findRowIndex(t);
56000         var cell = v.findCellIndex(t);
56001         if(row !== false && cell !== false){
56002             this.select(row, cell);
56003         }
56004     },
56005
56006     /**
56007      * Selects a cell.
56008      * @param {Number} rowIndex
56009      * @param {Number} collIndex
56010      */
56011     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56012         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56013             this.clearSelections();
56014             r = r || this.grid.dataSource.getAt(rowIndex);
56015             this.selection = {
56016                 record : r,
56017                 cell : [rowIndex, colIndex]
56018             };
56019             if(!preventViewNotify){
56020                 var v = this.grid.getView();
56021                 v.onCellSelect(rowIndex, colIndex);
56022                 if(preventFocus !== true){
56023                     v.focusCell(rowIndex, colIndex);
56024                 }
56025             }
56026             this.fireEvent("cellselect", this, rowIndex, colIndex);
56027             this.fireEvent("selectionchange", this, this.selection);
56028         }
56029     },
56030
56031         //private
56032     isSelectable : function(rowIndex, colIndex, cm){
56033         return !cm.isHidden(colIndex);
56034     },
56035
56036     /** @ignore */
56037     handleKeyDown : function(e){
56038         //Roo.log('Cell Sel Model handleKeyDown');
56039         if(!e.isNavKeyPress()){
56040             return;
56041         }
56042         var g = this.grid, s = this.selection;
56043         if(!s){
56044             e.stopEvent();
56045             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56046             if(cell){
56047                 this.select(cell[0], cell[1]);
56048             }
56049             return;
56050         }
56051         var sm = this;
56052         var walk = function(row, col, step){
56053             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56054         };
56055         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56056         var newCell;
56057
56058       
56059
56060         switch(k){
56061             case e.TAB:
56062                 // handled by onEditorKey
56063                 if (g.isEditor && g.editing) {
56064                     return;
56065                 }
56066                 if(e.shiftKey) {
56067                     newCell = walk(r, c-1, -1);
56068                 } else {
56069                     newCell = walk(r, c+1, 1);
56070                 }
56071                 break;
56072             
56073             case e.DOWN:
56074                newCell = walk(r+1, c, 1);
56075                 break;
56076             
56077             case e.UP:
56078                 newCell = walk(r-1, c, -1);
56079                 break;
56080             
56081             case e.RIGHT:
56082                 newCell = walk(r, c+1, 1);
56083                 break;
56084             
56085             case e.LEFT:
56086                 newCell = walk(r, c-1, -1);
56087                 break;
56088             
56089             case e.ENTER:
56090                 
56091                 if(g.isEditor && !g.editing){
56092                    g.startEditing(r, c);
56093                    e.stopEvent();
56094                    return;
56095                 }
56096                 
56097                 
56098              break;
56099         };
56100         if(newCell){
56101             this.select(newCell[0], newCell[1]);
56102             e.stopEvent();
56103             
56104         }
56105     },
56106
56107     acceptsNav : function(row, col, cm){
56108         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56109     },
56110     /**
56111      * Selects a cell.
56112      * @param {Number} field (not used) - as it's normally used as a listener
56113      * @param {Number} e - event - fake it by using
56114      *
56115      * var e = Roo.EventObjectImpl.prototype;
56116      * e.keyCode = e.TAB
56117      *
56118      * 
56119      */
56120     onEditorKey : function(field, e){
56121         
56122         var k = e.getKey(),
56123             newCell,
56124             g = this.grid,
56125             ed = g.activeEditor,
56126             forward = false;
56127         ///Roo.log('onEditorKey' + k);
56128         
56129         
56130         if (this.enter_is_tab && k == e.ENTER) {
56131             k = e.TAB;
56132         }
56133         
56134         if(k == e.TAB){
56135             if(e.shiftKey){
56136                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56137             }else{
56138                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56139                 forward = true;
56140             }
56141             
56142             e.stopEvent();
56143             
56144         } else if(k == e.ENTER &&  !e.ctrlKey){
56145             ed.completeEdit();
56146             e.stopEvent();
56147             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56148         
56149                 } else if(k == e.ESC){
56150             ed.cancelEdit();
56151         }
56152                 
56153         if (newCell) {
56154             var ecall = { cell : newCell, forward : forward };
56155             this.fireEvent('beforeeditnext', ecall );
56156             newCell = ecall.cell;
56157                         forward = ecall.forward;
56158         }
56159                 
56160         if(newCell){
56161             //Roo.log('next cell after edit');
56162             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56163         } else if (forward) {
56164             // tabbed past last
56165             this.fireEvent.defer(100, this, ['tabend',this]);
56166         }
56167     }
56168 });/*
56169  * Based on:
56170  * Ext JS Library 1.1.1
56171  * Copyright(c) 2006-2007, Ext JS, LLC.
56172  *
56173  * Originally Released Under LGPL - original licence link has changed is not relivant.
56174  *
56175  * Fork - LGPL
56176  * <script type="text/javascript">
56177  */
56178  
56179 /**
56180  * @class Roo.grid.EditorGrid
56181  * @extends Roo.grid.Grid
56182  * Class for creating and editable grid.
56183  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56184  * The container MUST have some type of size defined for the grid to fill. The container will be 
56185  * automatically set to position relative if it isn't already.
56186  * @param {Object} dataSource The data model to bind to
56187  * @param {Object} colModel The column model with info about this grid's columns
56188  */
56189 Roo.grid.EditorGrid = function(container, config){
56190     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56191     this.getGridEl().addClass("xedit-grid");
56192
56193     if(!this.selModel){
56194         this.selModel = new Roo.grid.CellSelectionModel();
56195     }
56196
56197     this.activeEditor = null;
56198
56199         this.addEvents({
56200             /**
56201              * @event beforeedit
56202              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56203              * <ul style="padding:5px;padding-left:16px;">
56204              * <li>grid - This grid</li>
56205              * <li>record - The record being edited</li>
56206              * <li>field - The field name being edited</li>
56207              * <li>value - The value for the field being edited.</li>
56208              * <li>row - The grid row index</li>
56209              * <li>column - The grid column index</li>
56210              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56211              * </ul>
56212              * @param {Object} e An edit event (see above for description)
56213              */
56214             "beforeedit" : true,
56215             /**
56216              * @event afteredit
56217              * Fires after a cell is edited. <br />
56218              * <ul style="padding:5px;padding-left:16px;">
56219              * <li>grid - This grid</li>
56220              * <li>record - The record being edited</li>
56221              * <li>field - The field name being edited</li>
56222              * <li>value - The value being set</li>
56223              * <li>originalValue - The original value for the field, before the edit.</li>
56224              * <li>row - The grid row index</li>
56225              * <li>column - The grid column index</li>
56226              * </ul>
56227              * @param {Object} e An edit event (see above for description)
56228              */
56229             "afteredit" : true,
56230             /**
56231              * @event validateedit
56232              * Fires after a cell is edited, but before the value is set in the record. 
56233          * You can use this to modify the value being set in the field, Return false
56234              * to cancel the change. The edit event object has the following properties <br />
56235              * <ul style="padding:5px;padding-left:16px;">
56236          * <li>editor - This editor</li>
56237              * <li>grid - This grid</li>
56238              * <li>record - The record being edited</li>
56239              * <li>field - The field name being edited</li>
56240              * <li>value - The value being set</li>
56241              * <li>originalValue - The original value for the field, before the edit.</li>
56242              * <li>row - The grid row index</li>
56243              * <li>column - The grid column index</li>
56244              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56245              * </ul>
56246              * @param {Object} e An edit event (see above for description)
56247              */
56248             "validateedit" : true
56249         });
56250     this.on("bodyscroll", this.stopEditing,  this);
56251     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56252 };
56253
56254 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56255     /**
56256      * @cfg {Number} clicksToEdit
56257      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56258      */
56259     clicksToEdit: 2,
56260
56261     // private
56262     isEditor : true,
56263     // private
56264     trackMouseOver: false, // causes very odd FF errors
56265
56266     onCellDblClick : function(g, row, col){
56267         this.startEditing(row, col);
56268     },
56269
56270     onEditComplete : function(ed, value, startValue){
56271         this.editing = false;
56272         this.activeEditor = null;
56273         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56274         var r = ed.record;
56275         var field = this.colModel.getDataIndex(ed.col);
56276         var e = {
56277             grid: this,
56278             record: r,
56279             field: field,
56280             originalValue: startValue,
56281             value: value,
56282             row: ed.row,
56283             column: ed.col,
56284             cancel:false,
56285             editor: ed
56286         };
56287         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56288         cell.show();
56289           
56290         if(String(value) !== String(startValue)){
56291             
56292             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56293                 r.set(field, e.value);
56294                 // if we are dealing with a combo box..
56295                 // then we also set the 'name' colum to be the displayField
56296                 if (ed.field.displayField && ed.field.name) {
56297                     r.set(ed.field.name, ed.field.el.dom.value);
56298                 }
56299                 
56300                 delete e.cancel; //?? why!!!
56301                 this.fireEvent("afteredit", e);
56302             }
56303         } else {
56304             this.fireEvent("afteredit", e); // always fire it!
56305         }
56306         this.view.focusCell(ed.row, ed.col);
56307     },
56308
56309     /**
56310      * Starts editing the specified for the specified row/column
56311      * @param {Number} rowIndex
56312      * @param {Number} colIndex
56313      */
56314     startEditing : function(row, col){
56315         this.stopEditing();
56316         if(this.colModel.isCellEditable(col, row)){
56317             this.view.ensureVisible(row, col, true);
56318           
56319             var r = this.dataSource.getAt(row);
56320             var field = this.colModel.getDataIndex(col);
56321             var cell = Roo.get(this.view.getCell(row,col));
56322             var e = {
56323                 grid: this,
56324                 record: r,
56325                 field: field,
56326                 value: r.data[field],
56327                 row: row,
56328                 column: col,
56329                 cancel:false 
56330             };
56331             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56332                 this.editing = true;
56333                 var ed = this.colModel.getCellEditor(col, row);
56334                 
56335                 if (!ed) {
56336                     return;
56337                 }
56338                 if(!ed.rendered){
56339                     ed.render(ed.parentEl || document.body);
56340                 }
56341                 ed.field.reset();
56342                
56343                 cell.hide();
56344                 
56345                 (function(){ // complex but required for focus issues in safari, ie and opera
56346                     ed.row = row;
56347                     ed.col = col;
56348                     ed.record = r;
56349                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56350                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56351                     this.activeEditor = ed;
56352                     var v = r.data[field];
56353                     ed.startEdit(this.view.getCell(row, col), v);
56354                     // combo's with 'displayField and name set
56355                     if (ed.field.displayField && ed.field.name) {
56356                         ed.field.el.dom.value = r.data[ed.field.name];
56357                     }
56358                     
56359                     
56360                 }).defer(50, this);
56361             }
56362         }
56363     },
56364         
56365     /**
56366      * Stops any active editing
56367      */
56368     stopEditing : function(){
56369         if(this.activeEditor){
56370             this.activeEditor.completeEdit();
56371         }
56372         this.activeEditor = null;
56373     },
56374         
56375          /**
56376      * Called to get grid's drag proxy text, by default returns this.ddText.
56377      * @return {String}
56378      */
56379     getDragDropText : function(){
56380         var count = this.selModel.getSelectedCell() ? 1 : 0;
56381         return String.format(this.ddText, count, count == 1 ? '' : 's');
56382     }
56383         
56384 });/*
56385  * Based on:
56386  * Ext JS Library 1.1.1
56387  * Copyright(c) 2006-2007, Ext JS, LLC.
56388  *
56389  * Originally Released Under LGPL - original licence link has changed is not relivant.
56390  *
56391  * Fork - LGPL
56392  * <script type="text/javascript">
56393  */
56394
56395 // private - not really -- you end up using it !
56396 // This is a support class used internally by the Grid components
56397
56398 /**
56399  * @class Roo.grid.GridEditor
56400  * @extends Roo.Editor
56401  * Class for creating and editable grid elements.
56402  * @param {Object} config any settings (must include field)
56403  */
56404 Roo.grid.GridEditor = function(field, config){
56405     if (!config && field.field) {
56406         config = field;
56407         field = Roo.factory(config.field, Roo.form);
56408     }
56409     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56410     field.monitorTab = false;
56411 };
56412
56413 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56414     
56415     /**
56416      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56417      */
56418     
56419     alignment: "tl-tl",
56420     autoSize: "width",
56421     hideEl : false,
56422     cls: "x-small-editor x-grid-editor",
56423     shim:false,
56424     shadow:"frame"
56425 });/*
56426  * Based on:
56427  * Ext JS Library 1.1.1
56428  * Copyright(c) 2006-2007, Ext JS, LLC.
56429  *
56430  * Originally Released Under LGPL - original licence link has changed is not relivant.
56431  *
56432  * Fork - LGPL
56433  * <script type="text/javascript">
56434  */
56435   
56436
56437   
56438 Roo.grid.PropertyRecord = Roo.data.Record.create([
56439     {name:'name',type:'string'},  'value'
56440 ]);
56441
56442
56443 Roo.grid.PropertyStore = function(grid, source){
56444     this.grid = grid;
56445     this.store = new Roo.data.Store({
56446         recordType : Roo.grid.PropertyRecord
56447     });
56448     this.store.on('update', this.onUpdate,  this);
56449     if(source){
56450         this.setSource(source);
56451     }
56452     Roo.grid.PropertyStore.superclass.constructor.call(this);
56453 };
56454
56455
56456
56457 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56458     setSource : function(o){
56459         this.source = o;
56460         this.store.removeAll();
56461         var data = [];
56462         for(var k in o){
56463             if(this.isEditableValue(o[k])){
56464                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56465             }
56466         }
56467         this.store.loadRecords({records: data}, {}, true);
56468     },
56469
56470     onUpdate : function(ds, record, type){
56471         if(type == Roo.data.Record.EDIT){
56472             var v = record.data['value'];
56473             var oldValue = record.modified['value'];
56474             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56475                 this.source[record.id] = v;
56476                 record.commit();
56477                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56478             }else{
56479                 record.reject();
56480             }
56481         }
56482     },
56483
56484     getProperty : function(row){
56485        return this.store.getAt(row);
56486     },
56487
56488     isEditableValue: function(val){
56489         if(val && val instanceof Date){
56490             return true;
56491         }else if(typeof val == 'object' || typeof val == 'function'){
56492             return false;
56493         }
56494         return true;
56495     },
56496
56497     setValue : function(prop, value){
56498         this.source[prop] = value;
56499         this.store.getById(prop).set('value', value);
56500     },
56501
56502     getSource : function(){
56503         return this.source;
56504     }
56505 });
56506
56507 Roo.grid.PropertyColumnModel = function(grid, store){
56508     this.grid = grid;
56509     var g = Roo.grid;
56510     g.PropertyColumnModel.superclass.constructor.call(this, [
56511         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56512         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56513     ]);
56514     this.store = store;
56515     this.bselect = Roo.DomHelper.append(document.body, {
56516         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56517             {tag: 'option', value: 'true', html: 'true'},
56518             {tag: 'option', value: 'false', html: 'false'}
56519         ]
56520     });
56521     Roo.id(this.bselect);
56522     var f = Roo.form;
56523     this.editors = {
56524         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56525         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56526         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56527         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56528         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56529     };
56530     this.renderCellDelegate = this.renderCell.createDelegate(this);
56531     this.renderPropDelegate = this.renderProp.createDelegate(this);
56532 };
56533
56534 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56535     
56536     
56537     nameText : 'Name',
56538     valueText : 'Value',
56539     
56540     dateFormat : 'm/j/Y',
56541     
56542     
56543     renderDate : function(dateVal){
56544         return dateVal.dateFormat(this.dateFormat);
56545     },
56546
56547     renderBool : function(bVal){
56548         return bVal ? 'true' : 'false';
56549     },
56550
56551     isCellEditable : function(colIndex, rowIndex){
56552         return colIndex == 1;
56553     },
56554
56555     getRenderer : function(col){
56556         return col == 1 ?
56557             this.renderCellDelegate : this.renderPropDelegate;
56558     },
56559
56560     renderProp : function(v){
56561         return this.getPropertyName(v);
56562     },
56563
56564     renderCell : function(val){
56565         var rv = val;
56566         if(val instanceof Date){
56567             rv = this.renderDate(val);
56568         }else if(typeof val == 'boolean'){
56569             rv = this.renderBool(val);
56570         }
56571         return Roo.util.Format.htmlEncode(rv);
56572     },
56573
56574     getPropertyName : function(name){
56575         var pn = this.grid.propertyNames;
56576         return pn && pn[name] ? pn[name] : name;
56577     },
56578
56579     getCellEditor : function(colIndex, rowIndex){
56580         var p = this.store.getProperty(rowIndex);
56581         var n = p.data['name'], val = p.data['value'];
56582         
56583         if(typeof(this.grid.customEditors[n]) == 'string'){
56584             return this.editors[this.grid.customEditors[n]];
56585         }
56586         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56587             return this.grid.customEditors[n];
56588         }
56589         if(val instanceof Date){
56590             return this.editors['date'];
56591         }else if(typeof val == 'number'){
56592             return this.editors['number'];
56593         }else if(typeof val == 'boolean'){
56594             return this.editors['boolean'];
56595         }else{
56596             return this.editors['string'];
56597         }
56598     }
56599 });
56600
56601 /**
56602  * @class Roo.grid.PropertyGrid
56603  * @extends Roo.grid.EditorGrid
56604  * This class represents the  interface of a component based property grid control.
56605  * <br><br>Usage:<pre><code>
56606  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56607       
56608  });
56609  // set any options
56610  grid.render();
56611  * </code></pre>
56612   
56613  * @constructor
56614  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56615  * The container MUST have some type of size defined for the grid to fill. The container will be
56616  * automatically set to position relative if it isn't already.
56617  * @param {Object} config A config object that sets properties on this grid.
56618  */
56619 Roo.grid.PropertyGrid = function(container, config){
56620     config = config || {};
56621     var store = new Roo.grid.PropertyStore(this);
56622     this.store = store;
56623     var cm = new Roo.grid.PropertyColumnModel(this, store);
56624     store.store.sort('name', 'ASC');
56625     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56626         ds: store.store,
56627         cm: cm,
56628         enableColLock:false,
56629         enableColumnMove:false,
56630         stripeRows:false,
56631         trackMouseOver: false,
56632         clicksToEdit:1
56633     }, config));
56634     this.getGridEl().addClass('x-props-grid');
56635     this.lastEditRow = null;
56636     this.on('columnresize', this.onColumnResize, this);
56637     this.addEvents({
56638          /**
56639              * @event beforepropertychange
56640              * Fires before a property changes (return false to stop?)
56641              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56642              * @param {String} id Record Id
56643              * @param {String} newval New Value
56644          * @param {String} oldval Old Value
56645              */
56646         "beforepropertychange": true,
56647         /**
56648              * @event propertychange
56649              * Fires after a property changes
56650              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56651              * @param {String} id Record Id
56652              * @param {String} newval New Value
56653          * @param {String} oldval Old Value
56654              */
56655         "propertychange": true
56656     });
56657     this.customEditors = this.customEditors || {};
56658 };
56659 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56660     
56661      /**
56662      * @cfg {Object} customEditors map of colnames=> custom editors.
56663      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56664      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56665      * false disables editing of the field.
56666          */
56667     
56668       /**
56669      * @cfg {Object} propertyNames map of property Names to their displayed value
56670          */
56671     
56672     render : function(){
56673         Roo.grid.PropertyGrid.superclass.render.call(this);
56674         this.autoSize.defer(100, this);
56675     },
56676
56677     autoSize : function(){
56678         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56679         if(this.view){
56680             this.view.fitColumns();
56681         }
56682     },
56683
56684     onColumnResize : function(){
56685         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56686         this.autoSize();
56687     },
56688     /**
56689      * Sets the data for the Grid
56690      * accepts a Key => Value object of all the elements avaiable.
56691      * @param {Object} data  to appear in grid.
56692      */
56693     setSource : function(source){
56694         this.store.setSource(source);
56695         //this.autoSize();
56696     },
56697     /**
56698      * Gets all the data from the grid.
56699      * @return {Object} data  data stored in grid
56700      */
56701     getSource : function(){
56702         return this.store.getSource();
56703     }
56704 });/*
56705   
56706  * Licence LGPL
56707  
56708  */
56709  
56710 /**
56711  * @class Roo.grid.Calendar
56712  * @extends Roo.util.Grid
56713  * This class extends the Grid to provide a calendar widget
56714  * <br><br>Usage:<pre><code>
56715  var grid = new Roo.grid.Calendar("my-container-id", {
56716      ds: myDataStore,
56717      cm: myColModel,
56718      selModel: mySelectionModel,
56719      autoSizeColumns: true,
56720      monitorWindowResize: false,
56721      trackMouseOver: true
56722      eventstore : real data store..
56723  });
56724  // set any options
56725  grid.render();
56726   
56727   * @constructor
56728  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56729  * The container MUST have some type of size defined for the grid to fill. The container will be
56730  * automatically set to position relative if it isn't already.
56731  * @param {Object} config A config object that sets properties on this grid.
56732  */
56733 Roo.grid.Calendar = function(container, config){
56734         // initialize the container
56735         this.container = Roo.get(container);
56736         this.container.update("");
56737         this.container.setStyle("overflow", "hidden");
56738     this.container.addClass('x-grid-container');
56739
56740     this.id = this.container.id;
56741
56742     Roo.apply(this, config);
56743     // check and correct shorthanded configs
56744     
56745     var rows = [];
56746     var d =1;
56747     for (var r = 0;r < 6;r++) {
56748         
56749         rows[r]=[];
56750         for (var c =0;c < 7;c++) {
56751             rows[r][c]= '';
56752         }
56753     }
56754     if (this.eventStore) {
56755         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56756         this.eventStore.on('load',this.onLoad, this);
56757         this.eventStore.on('beforeload',this.clearEvents, this);
56758          
56759     }
56760     
56761     this.dataSource = new Roo.data.Store({
56762             proxy: new Roo.data.MemoryProxy(rows),
56763             reader: new Roo.data.ArrayReader({}, [
56764                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56765     });
56766
56767     this.dataSource.load();
56768     this.ds = this.dataSource;
56769     this.ds.xmodule = this.xmodule || false;
56770     
56771     
56772     var cellRender = function(v,x,r)
56773     {
56774         return String.format(
56775             '<div class="fc-day  fc-widget-content"><div>' +
56776                 '<div class="fc-event-container"></div>' +
56777                 '<div class="fc-day-number">{0}</div>'+
56778                 
56779                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56780             '</div></div>', v);
56781     
56782     }
56783     
56784     
56785     this.colModel = new Roo.grid.ColumnModel( [
56786         {
56787             xtype: 'ColumnModel',
56788             xns: Roo.grid,
56789             dataIndex : 'weekday0',
56790             header : 'Sunday',
56791             renderer : cellRender
56792         },
56793         {
56794             xtype: 'ColumnModel',
56795             xns: Roo.grid,
56796             dataIndex : 'weekday1',
56797             header : 'Monday',
56798             renderer : cellRender
56799         },
56800         {
56801             xtype: 'ColumnModel',
56802             xns: Roo.grid,
56803             dataIndex : 'weekday2',
56804             header : 'Tuesday',
56805             renderer : cellRender
56806         },
56807         {
56808             xtype: 'ColumnModel',
56809             xns: Roo.grid,
56810             dataIndex : 'weekday3',
56811             header : 'Wednesday',
56812             renderer : cellRender
56813         },
56814         {
56815             xtype: 'ColumnModel',
56816             xns: Roo.grid,
56817             dataIndex : 'weekday4',
56818             header : 'Thursday',
56819             renderer : cellRender
56820         },
56821         {
56822             xtype: 'ColumnModel',
56823             xns: Roo.grid,
56824             dataIndex : 'weekday5',
56825             header : 'Friday',
56826             renderer : cellRender
56827         },
56828         {
56829             xtype: 'ColumnModel',
56830             xns: Roo.grid,
56831             dataIndex : 'weekday6',
56832             header : 'Saturday',
56833             renderer : cellRender
56834         }
56835     ]);
56836     this.cm = this.colModel;
56837     this.cm.xmodule = this.xmodule || false;
56838  
56839         
56840           
56841     //this.selModel = new Roo.grid.CellSelectionModel();
56842     //this.sm = this.selModel;
56843     //this.selModel.init(this);
56844     
56845     
56846     if(this.width){
56847         this.container.setWidth(this.width);
56848     }
56849
56850     if(this.height){
56851         this.container.setHeight(this.height);
56852     }
56853     /** @private */
56854         this.addEvents({
56855         // raw events
56856         /**
56857          * @event click
56858          * The raw click event for the entire grid.
56859          * @param {Roo.EventObject} e
56860          */
56861         "click" : true,
56862         /**
56863          * @event dblclick
56864          * The raw dblclick event for the entire grid.
56865          * @param {Roo.EventObject} e
56866          */
56867         "dblclick" : true,
56868         /**
56869          * @event contextmenu
56870          * The raw contextmenu event for the entire grid.
56871          * @param {Roo.EventObject} e
56872          */
56873         "contextmenu" : true,
56874         /**
56875          * @event mousedown
56876          * The raw mousedown event for the entire grid.
56877          * @param {Roo.EventObject} e
56878          */
56879         "mousedown" : true,
56880         /**
56881          * @event mouseup
56882          * The raw mouseup event for the entire grid.
56883          * @param {Roo.EventObject} e
56884          */
56885         "mouseup" : true,
56886         /**
56887          * @event mouseover
56888          * The raw mouseover event for the entire grid.
56889          * @param {Roo.EventObject} e
56890          */
56891         "mouseover" : true,
56892         /**
56893          * @event mouseout
56894          * The raw mouseout event for the entire grid.
56895          * @param {Roo.EventObject} e
56896          */
56897         "mouseout" : true,
56898         /**
56899          * @event keypress
56900          * The raw keypress event for the entire grid.
56901          * @param {Roo.EventObject} e
56902          */
56903         "keypress" : true,
56904         /**
56905          * @event keydown
56906          * The raw keydown event for the entire grid.
56907          * @param {Roo.EventObject} e
56908          */
56909         "keydown" : true,
56910
56911         // custom events
56912
56913         /**
56914          * @event cellclick
56915          * Fires when a cell is clicked
56916          * @param {Grid} this
56917          * @param {Number} rowIndex
56918          * @param {Number} columnIndex
56919          * @param {Roo.EventObject} e
56920          */
56921         "cellclick" : true,
56922         /**
56923          * @event celldblclick
56924          * Fires when a cell is double clicked
56925          * @param {Grid} this
56926          * @param {Number} rowIndex
56927          * @param {Number} columnIndex
56928          * @param {Roo.EventObject} e
56929          */
56930         "celldblclick" : true,
56931         /**
56932          * @event rowclick
56933          * Fires when a row is clicked
56934          * @param {Grid} this
56935          * @param {Number} rowIndex
56936          * @param {Roo.EventObject} e
56937          */
56938         "rowclick" : true,
56939         /**
56940          * @event rowdblclick
56941          * Fires when a row is double clicked
56942          * @param {Grid} this
56943          * @param {Number} rowIndex
56944          * @param {Roo.EventObject} e
56945          */
56946         "rowdblclick" : true,
56947         /**
56948          * @event headerclick
56949          * Fires when a header is clicked
56950          * @param {Grid} this
56951          * @param {Number} columnIndex
56952          * @param {Roo.EventObject} e
56953          */
56954         "headerclick" : true,
56955         /**
56956          * @event headerdblclick
56957          * Fires when a header cell is double clicked
56958          * @param {Grid} this
56959          * @param {Number} columnIndex
56960          * @param {Roo.EventObject} e
56961          */
56962         "headerdblclick" : true,
56963         /**
56964          * @event rowcontextmenu
56965          * Fires when a row is right clicked
56966          * @param {Grid} this
56967          * @param {Number} rowIndex
56968          * @param {Roo.EventObject} e
56969          */
56970         "rowcontextmenu" : true,
56971         /**
56972          * @event cellcontextmenu
56973          * Fires when a cell is right clicked
56974          * @param {Grid} this
56975          * @param {Number} rowIndex
56976          * @param {Number} cellIndex
56977          * @param {Roo.EventObject} e
56978          */
56979          "cellcontextmenu" : true,
56980         /**
56981          * @event headercontextmenu
56982          * Fires when a header is right clicked
56983          * @param {Grid} this
56984          * @param {Number} columnIndex
56985          * @param {Roo.EventObject} e
56986          */
56987         "headercontextmenu" : true,
56988         /**
56989          * @event bodyscroll
56990          * Fires when the body element is scrolled
56991          * @param {Number} scrollLeft
56992          * @param {Number} scrollTop
56993          */
56994         "bodyscroll" : true,
56995         /**
56996          * @event columnresize
56997          * Fires when the user resizes a column
56998          * @param {Number} columnIndex
56999          * @param {Number} newSize
57000          */
57001         "columnresize" : true,
57002         /**
57003          * @event columnmove
57004          * Fires when the user moves a column
57005          * @param {Number} oldIndex
57006          * @param {Number} newIndex
57007          */
57008         "columnmove" : true,
57009         /**
57010          * @event startdrag
57011          * Fires when row(s) start being dragged
57012          * @param {Grid} this
57013          * @param {Roo.GridDD} dd The drag drop object
57014          * @param {event} e The raw browser event
57015          */
57016         "startdrag" : true,
57017         /**
57018          * @event enddrag
57019          * Fires when a drag operation is complete
57020          * @param {Grid} this
57021          * @param {Roo.GridDD} dd The drag drop object
57022          * @param {event} e The raw browser event
57023          */
57024         "enddrag" : true,
57025         /**
57026          * @event dragdrop
57027          * Fires when dragged row(s) are dropped on a valid DD target
57028          * @param {Grid} this
57029          * @param {Roo.GridDD} dd The drag drop object
57030          * @param {String} targetId The target drag drop object
57031          * @param {event} e The raw browser event
57032          */
57033         "dragdrop" : true,
57034         /**
57035          * @event dragover
57036          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57037          * @param {Grid} this
57038          * @param {Roo.GridDD} dd The drag drop object
57039          * @param {String} targetId The target drag drop object
57040          * @param {event} e The raw browser event
57041          */
57042         "dragover" : true,
57043         /**
57044          * @event dragenter
57045          *  Fires when the dragged row(s) first cross another DD target while being dragged
57046          * @param {Grid} this
57047          * @param {Roo.GridDD} dd The drag drop object
57048          * @param {String} targetId The target drag drop object
57049          * @param {event} e The raw browser event
57050          */
57051         "dragenter" : true,
57052         /**
57053          * @event dragout
57054          * Fires when the dragged row(s) leave another DD target while being dragged
57055          * @param {Grid} this
57056          * @param {Roo.GridDD} dd The drag drop object
57057          * @param {String} targetId The target drag drop object
57058          * @param {event} e The raw browser event
57059          */
57060         "dragout" : true,
57061         /**
57062          * @event rowclass
57063          * Fires when a row is rendered, so you can change add a style to it.
57064          * @param {GridView} gridview   The grid view
57065          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57066          */
57067         'rowclass' : true,
57068
57069         /**
57070          * @event render
57071          * Fires when the grid is rendered
57072          * @param {Grid} grid
57073          */
57074         'render' : true,
57075             /**
57076              * @event select
57077              * Fires when a date is selected
57078              * @param {DatePicker} this
57079              * @param {Date} date The selected date
57080              */
57081         'select': true,
57082         /**
57083              * @event monthchange
57084              * Fires when the displayed month changes 
57085              * @param {DatePicker} this
57086              * @param {Date} date The selected month
57087              */
57088         'monthchange': true,
57089         /**
57090              * @event evententer
57091              * Fires when mouse over an event
57092              * @param {Calendar} this
57093              * @param {event} Event
57094              */
57095         'evententer': true,
57096         /**
57097              * @event eventleave
57098              * Fires when the mouse leaves an
57099              * @param {Calendar} this
57100              * @param {event}
57101              */
57102         'eventleave': true,
57103         /**
57104              * @event eventclick
57105              * Fires when the mouse click an
57106              * @param {Calendar} this
57107              * @param {event}
57108              */
57109         'eventclick': true,
57110         /**
57111              * @event eventrender
57112              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57113              * @param {Calendar} this
57114              * @param {data} data to be modified
57115              */
57116         'eventrender': true
57117         
57118     });
57119
57120     Roo.grid.Grid.superclass.constructor.call(this);
57121     this.on('render', function() {
57122         this.view.el.addClass('x-grid-cal'); 
57123         
57124         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57125
57126     },this);
57127     
57128     if (!Roo.grid.Calendar.style) {
57129         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57130             
57131             
57132             '.x-grid-cal .x-grid-col' :  {
57133                 height: 'auto !important',
57134                 'vertical-align': 'top'
57135             },
57136             '.x-grid-cal  .fc-event-hori' : {
57137                 height: '14px'
57138             }
57139              
57140             
57141         }, Roo.id());
57142     }
57143
57144     
57145     
57146 };
57147 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57148     /**
57149      * @cfg {Store} eventStore The store that loads events.
57150      */
57151     eventStore : 25,
57152
57153      
57154     activeDate : false,
57155     startDay : 0,
57156     autoWidth : true,
57157     monitorWindowResize : false,
57158
57159     
57160     resizeColumns : function() {
57161         var col = (this.view.el.getWidth() / 7) - 3;
57162         // loop through cols, and setWidth
57163         for(var i =0 ; i < 7 ; i++){
57164             this.cm.setColumnWidth(i, col);
57165         }
57166     },
57167      setDate :function(date) {
57168         
57169         Roo.log('setDate?');
57170         
57171         this.resizeColumns();
57172         var vd = this.activeDate;
57173         this.activeDate = date;
57174 //        if(vd && this.el){
57175 //            var t = date.getTime();
57176 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57177 //                Roo.log('using add remove');
57178 //                
57179 //                this.fireEvent('monthchange', this, date);
57180 //                
57181 //                this.cells.removeClass("fc-state-highlight");
57182 //                this.cells.each(function(c){
57183 //                   if(c.dateValue == t){
57184 //                       c.addClass("fc-state-highlight");
57185 //                       setTimeout(function(){
57186 //                            try{c.dom.firstChild.focus();}catch(e){}
57187 //                       }, 50);
57188 //                       return false;
57189 //                   }
57190 //                   return true;
57191 //                });
57192 //                return;
57193 //            }
57194 //        }
57195         
57196         var days = date.getDaysInMonth();
57197         
57198         var firstOfMonth = date.getFirstDateOfMonth();
57199         var startingPos = firstOfMonth.getDay()-this.startDay;
57200         
57201         if(startingPos < this.startDay){
57202             startingPos += 7;
57203         }
57204         
57205         var pm = date.add(Date.MONTH, -1);
57206         var prevStart = pm.getDaysInMonth()-startingPos;
57207 //        
57208         
57209         
57210         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57211         
57212         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57213         //this.cells.addClassOnOver('fc-state-hover');
57214         
57215         var cells = this.cells.elements;
57216         var textEls = this.textNodes;
57217         
57218         //Roo.each(cells, function(cell){
57219         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57220         //});
57221         
57222         days += startingPos;
57223
57224         // convert everything to numbers so it's fast
57225         var day = 86400000;
57226         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57227         //Roo.log(d);
57228         //Roo.log(pm);
57229         //Roo.log(prevStart);
57230         
57231         var today = new Date().clearTime().getTime();
57232         var sel = date.clearTime().getTime();
57233         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57234         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57235         var ddMatch = this.disabledDatesRE;
57236         var ddText = this.disabledDatesText;
57237         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57238         var ddaysText = this.disabledDaysText;
57239         var format = this.format;
57240         
57241         var setCellClass = function(cal, cell){
57242             
57243             //Roo.log('set Cell Class');
57244             cell.title = "";
57245             var t = d.getTime();
57246             
57247             //Roo.log(d);
57248             
57249             
57250             cell.dateValue = t;
57251             if(t == today){
57252                 cell.className += " fc-today";
57253                 cell.className += " fc-state-highlight";
57254                 cell.title = cal.todayText;
57255             }
57256             if(t == sel){
57257                 // disable highlight in other month..
57258                 cell.className += " fc-state-highlight";
57259                 
57260             }
57261             // disabling
57262             if(t < min) {
57263                 //cell.className = " fc-state-disabled";
57264                 cell.title = cal.minText;
57265                 return;
57266             }
57267             if(t > max) {
57268                 //cell.className = " fc-state-disabled";
57269                 cell.title = cal.maxText;
57270                 return;
57271             }
57272             if(ddays){
57273                 if(ddays.indexOf(d.getDay()) != -1){
57274                     // cell.title = ddaysText;
57275                    // cell.className = " fc-state-disabled";
57276                 }
57277             }
57278             if(ddMatch && format){
57279                 var fvalue = d.dateFormat(format);
57280                 if(ddMatch.test(fvalue)){
57281                     cell.title = ddText.replace("%0", fvalue);
57282                    cell.className = " fc-state-disabled";
57283                 }
57284             }
57285             
57286             if (!cell.initialClassName) {
57287                 cell.initialClassName = cell.dom.className;
57288             }
57289             
57290             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57291         };
57292
57293         var i = 0;
57294         
57295         for(; i < startingPos; i++) {
57296             cells[i].dayName =  (++prevStart);
57297             Roo.log(textEls[i]);
57298             d.setDate(d.getDate()+1);
57299             
57300             //cells[i].className = "fc-past fc-other-month";
57301             setCellClass(this, cells[i]);
57302         }
57303         
57304         var intDay = 0;
57305         
57306         for(; i < days; i++){
57307             intDay = i - startingPos + 1;
57308             cells[i].dayName =  (intDay);
57309             d.setDate(d.getDate()+1);
57310             
57311             cells[i].className = ''; // "x-date-active";
57312             setCellClass(this, cells[i]);
57313         }
57314         var extraDays = 0;
57315         
57316         for(; i < 42; i++) {
57317             //textEls[i].innerHTML = (++extraDays);
57318             
57319             d.setDate(d.getDate()+1);
57320             cells[i].dayName = (++extraDays);
57321             cells[i].className = "fc-future fc-other-month";
57322             setCellClass(this, cells[i]);
57323         }
57324         
57325         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57326         
57327         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57328         
57329         // this will cause all the cells to mis
57330         var rows= [];
57331         var i =0;
57332         for (var r = 0;r < 6;r++) {
57333             for (var c =0;c < 7;c++) {
57334                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57335             }    
57336         }
57337         
57338         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57339         for(i=0;i<cells.length;i++) {
57340             
57341             this.cells.elements[i].dayName = cells[i].dayName ;
57342             this.cells.elements[i].className = cells[i].className;
57343             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57344             this.cells.elements[i].title = cells[i].title ;
57345             this.cells.elements[i].dateValue = cells[i].dateValue ;
57346         }
57347         
57348         
57349         
57350         
57351         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57352         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57353         
57354         ////if(totalRows != 6){
57355             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57356            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57357        // }
57358         
57359         this.fireEvent('monthchange', this, date);
57360         
57361         
57362     },
57363  /**
57364      * Returns the grid's SelectionModel.
57365      * @return {SelectionModel}
57366      */
57367     getSelectionModel : function(){
57368         if(!this.selModel){
57369             this.selModel = new Roo.grid.CellSelectionModel();
57370         }
57371         return this.selModel;
57372     },
57373
57374     load: function() {
57375         this.eventStore.load()
57376         
57377         
57378         
57379     },
57380     
57381     findCell : function(dt) {
57382         dt = dt.clearTime().getTime();
57383         var ret = false;
57384         this.cells.each(function(c){
57385             //Roo.log("check " +c.dateValue + '?=' + dt);
57386             if(c.dateValue == dt){
57387                 ret = c;
57388                 return false;
57389             }
57390             return true;
57391         });
57392         
57393         return ret;
57394     },
57395     
57396     findCells : function(rec) {
57397         var s = rec.data.start_dt.clone().clearTime().getTime();
57398        // Roo.log(s);
57399         var e= rec.data.end_dt.clone().clearTime().getTime();
57400        // Roo.log(e);
57401         var ret = [];
57402         this.cells.each(function(c){
57403              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57404             
57405             if(c.dateValue > e){
57406                 return ;
57407             }
57408             if(c.dateValue < s){
57409                 return ;
57410             }
57411             ret.push(c);
57412         });
57413         
57414         return ret;    
57415     },
57416     
57417     findBestRow: function(cells)
57418     {
57419         var ret = 0;
57420         
57421         for (var i =0 ; i < cells.length;i++) {
57422             ret  = Math.max(cells[i].rows || 0,ret);
57423         }
57424         return ret;
57425         
57426     },
57427     
57428     
57429     addItem : function(rec)
57430     {
57431         // look for vertical location slot in
57432         var cells = this.findCells(rec);
57433         
57434         rec.row = this.findBestRow(cells);
57435         
57436         // work out the location.
57437         
57438         var crow = false;
57439         var rows = [];
57440         for(var i =0; i < cells.length; i++) {
57441             if (!crow) {
57442                 crow = {
57443                     start : cells[i],
57444                     end :  cells[i]
57445                 };
57446                 continue;
57447             }
57448             if (crow.start.getY() == cells[i].getY()) {
57449                 // on same row.
57450                 crow.end = cells[i];
57451                 continue;
57452             }
57453             // different row.
57454             rows.push(crow);
57455             crow = {
57456                 start: cells[i],
57457                 end : cells[i]
57458             };
57459             
57460         }
57461         
57462         rows.push(crow);
57463         rec.els = [];
57464         rec.rows = rows;
57465         rec.cells = cells;
57466         for (var i = 0; i < cells.length;i++) {
57467             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57468             
57469         }
57470         
57471         
57472     },
57473     
57474     clearEvents: function() {
57475         
57476         if (!this.eventStore.getCount()) {
57477             return;
57478         }
57479         // reset number of rows in cells.
57480         Roo.each(this.cells.elements, function(c){
57481             c.rows = 0;
57482         });
57483         
57484         this.eventStore.each(function(e) {
57485             this.clearEvent(e);
57486         },this);
57487         
57488     },
57489     
57490     clearEvent : function(ev)
57491     {
57492         if (ev.els) {
57493             Roo.each(ev.els, function(el) {
57494                 el.un('mouseenter' ,this.onEventEnter, this);
57495                 el.un('mouseleave' ,this.onEventLeave, this);
57496                 el.remove();
57497             },this);
57498             ev.els = [];
57499         }
57500     },
57501     
57502     
57503     renderEvent : function(ev,ctr) {
57504         if (!ctr) {
57505              ctr = this.view.el.select('.fc-event-container',true).first();
57506         }
57507         
57508          
57509         this.clearEvent(ev);
57510             //code
57511        
57512         
57513         
57514         ev.els = [];
57515         var cells = ev.cells;
57516         var rows = ev.rows;
57517         this.fireEvent('eventrender', this, ev);
57518         
57519         for(var i =0; i < rows.length; i++) {
57520             
57521             cls = '';
57522             if (i == 0) {
57523                 cls += ' fc-event-start';
57524             }
57525             if ((i+1) == rows.length) {
57526                 cls += ' fc-event-end';
57527             }
57528             
57529             //Roo.log(ev.data);
57530             // how many rows should it span..
57531             var cg = this.eventTmpl.append(ctr,Roo.apply({
57532                 fccls : cls
57533                 
57534             }, ev.data) , true);
57535             
57536             
57537             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57538             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57539             cg.on('click', this.onEventClick, this, ev);
57540             
57541             ev.els.push(cg);
57542             
57543             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57544             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57545             //Roo.log(cg);
57546              
57547             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57548             cg.setWidth(ebox.right - sbox.x -2);
57549         }
57550     },
57551     
57552     renderEvents: function()
57553     {   
57554         // first make sure there is enough space..
57555         
57556         if (!this.eventTmpl) {
57557             this.eventTmpl = new Roo.Template(
57558                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57559                     '<div class="fc-event-inner">' +
57560                         '<span class="fc-event-time">{time}</span>' +
57561                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57562                     '</div>' +
57563                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57564                 '</div>'
57565             );
57566                 
57567         }
57568                
57569         
57570         
57571         this.cells.each(function(c) {
57572             //Roo.log(c.select('.fc-day-content div',true).first());
57573             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57574         });
57575         
57576         var ctr = this.view.el.select('.fc-event-container',true).first();
57577         
57578         var cls;
57579         this.eventStore.each(function(ev){
57580             
57581             this.renderEvent(ev);
57582              
57583              
57584         }, this);
57585         this.view.layout();
57586         
57587     },
57588     
57589     onEventEnter: function (e, el,event,d) {
57590         this.fireEvent('evententer', this, el, event);
57591     },
57592     
57593     onEventLeave: function (e, el,event,d) {
57594         this.fireEvent('eventleave', this, el, event);
57595     },
57596     
57597     onEventClick: function (e, el,event,d) {
57598         this.fireEvent('eventclick', this, el, event);
57599     },
57600     
57601     onMonthChange: function () {
57602         this.store.load();
57603     },
57604     
57605     onLoad: function () {
57606         
57607         //Roo.log('calendar onload');
57608 //         
57609         if(this.eventStore.getCount() > 0){
57610             
57611            
57612             
57613             this.eventStore.each(function(d){
57614                 
57615                 
57616                 // FIXME..
57617                 var add =   d.data;
57618                 if (typeof(add.end_dt) == 'undefined')  {
57619                     Roo.log("Missing End time in calendar data: ");
57620                     Roo.log(d);
57621                     return;
57622                 }
57623                 if (typeof(add.start_dt) == 'undefined')  {
57624                     Roo.log("Missing Start time in calendar data: ");
57625                     Roo.log(d);
57626                     return;
57627                 }
57628                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57629                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57630                 add.id = add.id || d.id;
57631                 add.title = add.title || '??';
57632                 
57633                 this.addItem(d);
57634                 
57635              
57636             },this);
57637         }
57638         
57639         this.renderEvents();
57640     }
57641     
57642
57643 });
57644 /*
57645  grid : {
57646                 xtype: 'Grid',
57647                 xns: Roo.grid,
57648                 listeners : {
57649                     render : function ()
57650                     {
57651                         _this.grid = this;
57652                         
57653                         if (!this.view.el.hasClass('course-timesheet')) {
57654                             this.view.el.addClass('course-timesheet');
57655                         }
57656                         if (this.tsStyle) {
57657                             this.ds.load({});
57658                             return; 
57659                         }
57660                         Roo.log('width');
57661                         Roo.log(_this.grid.view.el.getWidth());
57662                         
57663                         
57664                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57665                             '.course-timesheet .x-grid-row' : {
57666                                 height: '80px'
57667                             },
57668                             '.x-grid-row td' : {
57669                                 'vertical-align' : 0
57670                             },
57671                             '.course-edit-link' : {
57672                                 'color' : 'blue',
57673                                 'text-overflow' : 'ellipsis',
57674                                 'overflow' : 'hidden',
57675                                 'white-space' : 'nowrap',
57676                                 'cursor' : 'pointer'
57677                             },
57678                             '.sub-link' : {
57679                                 'color' : 'green'
57680                             },
57681                             '.de-act-sup-link' : {
57682                                 'color' : 'purple',
57683                                 'text-decoration' : 'line-through'
57684                             },
57685                             '.de-act-link' : {
57686                                 'color' : 'red',
57687                                 'text-decoration' : 'line-through'
57688                             },
57689                             '.course-timesheet .course-highlight' : {
57690                                 'border-top-style': 'dashed !important',
57691                                 'border-bottom-bottom': 'dashed !important'
57692                             },
57693                             '.course-timesheet .course-item' : {
57694                                 'font-family'   : 'tahoma, arial, helvetica',
57695                                 'font-size'     : '11px',
57696                                 'overflow'      : 'hidden',
57697                                 'padding-left'  : '10px',
57698                                 'padding-right' : '10px',
57699                                 'padding-top' : '10px' 
57700                             }
57701                             
57702                         }, Roo.id());
57703                                 this.ds.load({});
57704                     }
57705                 },
57706                 autoWidth : true,
57707                 monitorWindowResize : false,
57708                 cellrenderer : function(v,x,r)
57709                 {
57710                     return v;
57711                 },
57712                 sm : {
57713                     xtype: 'CellSelectionModel',
57714                     xns: Roo.grid
57715                 },
57716                 dataSource : {
57717                     xtype: 'Store',
57718                     xns: Roo.data,
57719                     listeners : {
57720                         beforeload : function (_self, options)
57721                         {
57722                             options.params = options.params || {};
57723                             options.params._month = _this.monthField.getValue();
57724                             options.params.limit = 9999;
57725                             options.params['sort'] = 'when_dt';    
57726                             options.params['dir'] = 'ASC';    
57727                             this.proxy.loadResponse = this.loadResponse;
57728                             Roo.log("load?");
57729                             //this.addColumns();
57730                         },
57731                         load : function (_self, records, options)
57732                         {
57733                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57734                                 // if you click on the translation.. you can edit it...
57735                                 var el = Roo.get(this);
57736                                 var id = el.dom.getAttribute('data-id');
57737                                 var d = el.dom.getAttribute('data-date');
57738                                 var t = el.dom.getAttribute('data-time');
57739                                 //var id = this.child('span').dom.textContent;
57740                                 
57741                                 //Roo.log(this);
57742                                 Pman.Dialog.CourseCalendar.show({
57743                                     id : id,
57744                                     when_d : d,
57745                                     when_t : t,
57746                                     productitem_active : id ? 1 : 0
57747                                 }, function() {
57748                                     _this.grid.ds.load({});
57749                                 });
57750                            
57751                            });
57752                            
57753                            _this.panel.fireEvent('resize', [ '', '' ]);
57754                         }
57755                     },
57756                     loadResponse : function(o, success, response){
57757                             // this is overridden on before load..
57758                             
57759                             Roo.log("our code?");       
57760                             //Roo.log(success);
57761                             //Roo.log(response)
57762                             delete this.activeRequest;
57763                             if(!success){
57764                                 this.fireEvent("loadexception", this, o, response);
57765                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57766                                 return;
57767                             }
57768                             var result;
57769                             try {
57770                                 result = o.reader.read(response);
57771                             }catch(e){
57772                                 Roo.log("load exception?");
57773                                 this.fireEvent("loadexception", this, o, response, e);
57774                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57775                                 return;
57776                             }
57777                             Roo.log("ready...");        
57778                             // loop through result.records;
57779                             // and set this.tdate[date] = [] << array of records..
57780                             _this.tdata  = {};
57781                             Roo.each(result.records, function(r){
57782                                 //Roo.log(r.data);
57783                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57784                                     _this.tdata[r.data.when_dt.format('j')] = [];
57785                                 }
57786                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57787                             });
57788                             
57789                             //Roo.log(_this.tdata);
57790                             
57791                             result.records = [];
57792                             result.totalRecords = 6;
57793                     
57794                             // let's generate some duumy records for the rows.
57795                             //var st = _this.dateField.getValue();
57796                             
57797                             // work out monday..
57798                             //st = st.add(Date.DAY, -1 * st.format('w'));
57799                             
57800                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57801                             
57802                             var firstOfMonth = date.getFirstDayOfMonth();
57803                             var days = date.getDaysInMonth();
57804                             var d = 1;
57805                             var firstAdded = false;
57806                             for (var i = 0; i < result.totalRecords ; i++) {
57807                                 //var d= st.add(Date.DAY, i);
57808                                 var row = {};
57809                                 var added = 0;
57810                                 for(var w = 0 ; w < 7 ; w++){
57811                                     if(!firstAdded && firstOfMonth != w){
57812                                         continue;
57813                                     }
57814                                     if(d > days){
57815                                         continue;
57816                                     }
57817                                     firstAdded = true;
57818                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57819                                     row['weekday'+w] = String.format(
57820                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57821                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57822                                                     d,
57823                                                     date.format('Y-m-')+dd
57824                                                 );
57825                                     added++;
57826                                     if(typeof(_this.tdata[d]) != 'undefined'){
57827                                         Roo.each(_this.tdata[d], function(r){
57828                                             var is_sub = '';
57829                                             var deactive = '';
57830                                             var id = r.id;
57831                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57832                                             if(r.parent_id*1>0){
57833                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57834                                                 id = r.parent_id;
57835                                             }
57836                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57837                                                 deactive = 'de-act-link';
57838                                             }
57839                                             
57840                                             row['weekday'+w] += String.format(
57841                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57842                                                     id, //0
57843                                                     r.product_id_name, //1
57844                                                     r.when_dt.format('h:ia'), //2
57845                                                     is_sub, //3
57846                                                     deactive, //4
57847                                                     desc // 5
57848                                             );
57849                                         });
57850                                     }
57851                                     d++;
57852                                 }
57853                                 
57854                                 // only do this if something added..
57855                                 if(added > 0){ 
57856                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57857                                 }
57858                                 
57859                                 
57860                                 // push it twice. (second one with an hour..
57861                                 
57862                             }
57863                             //Roo.log(result);
57864                             this.fireEvent("load", this, o, o.request.arg);
57865                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57866                         },
57867                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57868                     proxy : {
57869                         xtype: 'HttpProxy',
57870                         xns: Roo.data,
57871                         method : 'GET',
57872                         url : baseURL + '/Roo/Shop_course.php'
57873                     },
57874                     reader : {
57875                         xtype: 'JsonReader',
57876                         xns: Roo.data,
57877                         id : 'id',
57878                         fields : [
57879                             {
57880                                 'name': 'id',
57881                                 'type': 'int'
57882                             },
57883                             {
57884                                 'name': 'when_dt',
57885                                 'type': 'string'
57886                             },
57887                             {
57888                                 'name': 'end_dt',
57889                                 'type': 'string'
57890                             },
57891                             {
57892                                 'name': 'parent_id',
57893                                 'type': 'int'
57894                             },
57895                             {
57896                                 'name': 'product_id',
57897                                 'type': 'int'
57898                             },
57899                             {
57900                                 'name': 'productitem_id',
57901                                 'type': 'int'
57902                             },
57903                             {
57904                                 'name': 'guid',
57905                                 'type': 'int'
57906                             }
57907                         ]
57908                     }
57909                 },
57910                 toolbar : {
57911                     xtype: 'Toolbar',
57912                     xns: Roo,
57913                     items : [
57914                         {
57915                             xtype: 'Button',
57916                             xns: Roo.Toolbar,
57917                             listeners : {
57918                                 click : function (_self, e)
57919                                 {
57920                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57921                                     sd.setMonth(sd.getMonth()-1);
57922                                     _this.monthField.setValue(sd.format('Y-m-d'));
57923                                     _this.grid.ds.load({});
57924                                 }
57925                             },
57926                             text : "Back"
57927                         },
57928                         {
57929                             xtype: 'Separator',
57930                             xns: Roo.Toolbar
57931                         },
57932                         {
57933                             xtype: 'MonthField',
57934                             xns: Roo.form,
57935                             listeners : {
57936                                 render : function (_self)
57937                                 {
57938                                     _this.monthField = _self;
57939                                    // _this.monthField.set  today
57940                                 },
57941                                 select : function (combo, date)
57942                                 {
57943                                     _this.grid.ds.load({});
57944                                 }
57945                             },
57946                             value : (function() { return new Date(); })()
57947                         },
57948                         {
57949                             xtype: 'Separator',
57950                             xns: Roo.Toolbar
57951                         },
57952                         {
57953                             xtype: 'TextItem',
57954                             xns: Roo.Toolbar,
57955                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57956                         },
57957                         {
57958                             xtype: 'Fill',
57959                             xns: Roo.Toolbar
57960                         },
57961                         {
57962                             xtype: 'Button',
57963                             xns: Roo.Toolbar,
57964                             listeners : {
57965                                 click : function (_self, e)
57966                                 {
57967                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57968                                     sd.setMonth(sd.getMonth()+1);
57969                                     _this.monthField.setValue(sd.format('Y-m-d'));
57970                                     _this.grid.ds.load({});
57971                                 }
57972                             },
57973                             text : "Next"
57974                         }
57975                     ]
57976                 },
57977                  
57978             }
57979         };
57980         
57981         *//*
57982  * Based on:
57983  * Ext JS Library 1.1.1
57984  * Copyright(c) 2006-2007, Ext JS, LLC.
57985  *
57986  * Originally Released Under LGPL - original licence link has changed is not relivant.
57987  *
57988  * Fork - LGPL
57989  * <script type="text/javascript">
57990  */
57991  
57992 /**
57993  * @class Roo.LoadMask
57994  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57995  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57996  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57997  * element's UpdateManager load indicator and will be destroyed after the initial load.
57998  * @constructor
57999  * Create a new LoadMask
58000  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58001  * @param {Object} config The config object
58002  */
58003 Roo.LoadMask = function(el, config){
58004     this.el = Roo.get(el);
58005     Roo.apply(this, config);
58006     if(this.store){
58007         this.store.on('beforeload', this.onBeforeLoad, this);
58008         this.store.on('load', this.onLoad, this);
58009         this.store.on('loadexception', this.onLoadException, this);
58010         this.removeMask = false;
58011     }else{
58012         var um = this.el.getUpdateManager();
58013         um.showLoadIndicator = false; // disable the default indicator
58014         um.on('beforeupdate', this.onBeforeLoad, this);
58015         um.on('update', this.onLoad, this);
58016         um.on('failure', this.onLoad, this);
58017         this.removeMask = true;
58018     }
58019 };
58020
58021 Roo.LoadMask.prototype = {
58022     /**
58023      * @cfg {Boolean} removeMask
58024      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58025      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58026      */
58027     /**
58028      * @cfg {String} msg
58029      * The text to display in a centered loading message box (defaults to 'Loading...')
58030      */
58031     msg : 'Loading...',
58032     /**
58033      * @cfg {String} msgCls
58034      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58035      */
58036     msgCls : 'x-mask-loading',
58037
58038     /**
58039      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58040      * @type Boolean
58041      */
58042     disabled: false,
58043
58044     /**
58045      * Disables the mask to prevent it from being displayed
58046      */
58047     disable : function(){
58048        this.disabled = true;
58049     },
58050
58051     /**
58052      * Enables the mask so that it can be displayed
58053      */
58054     enable : function(){
58055         this.disabled = false;
58056     },
58057     
58058     onLoadException : function()
58059     {
58060         Roo.log(arguments);
58061         
58062         if (typeof(arguments[3]) != 'undefined') {
58063             Roo.MessageBox.alert("Error loading",arguments[3]);
58064         } 
58065         /*
58066         try {
58067             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58068                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58069             }   
58070         } catch(e) {
58071             
58072         }
58073         */
58074     
58075         
58076         
58077         this.el.unmask(this.removeMask);
58078     },
58079     // private
58080     onLoad : function()
58081     {
58082         this.el.unmask(this.removeMask);
58083     },
58084
58085     // private
58086     onBeforeLoad : function(){
58087         if(!this.disabled){
58088             this.el.mask(this.msg, this.msgCls);
58089         }
58090     },
58091
58092     // private
58093     destroy : function(){
58094         if(this.store){
58095             this.store.un('beforeload', this.onBeforeLoad, this);
58096             this.store.un('load', this.onLoad, this);
58097             this.store.un('loadexception', this.onLoadException, this);
58098         }else{
58099             var um = this.el.getUpdateManager();
58100             um.un('beforeupdate', this.onBeforeLoad, this);
58101             um.un('update', this.onLoad, this);
58102             um.un('failure', this.onLoad, this);
58103         }
58104     }
58105 };/*
58106  * Based on:
58107  * Ext JS Library 1.1.1
58108  * Copyright(c) 2006-2007, Ext JS, LLC.
58109  *
58110  * Originally Released Under LGPL - original licence link has changed is not relivant.
58111  *
58112  * Fork - LGPL
58113  * <script type="text/javascript">
58114  */
58115
58116
58117 /**
58118  * @class Roo.XTemplate
58119  * @extends Roo.Template
58120  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58121 <pre><code>
58122 var t = new Roo.XTemplate(
58123         '&lt;select name="{name}"&gt;',
58124                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58125         '&lt;/select&gt;'
58126 );
58127  
58128 // then append, applying the master template values
58129  </code></pre>
58130  *
58131  * Supported features:
58132  *
58133  *  Tags:
58134
58135 <pre><code>
58136       {a_variable} - output encoded.
58137       {a_variable.format:("Y-m-d")} - call a method on the variable
58138       {a_variable:raw} - unencoded output
58139       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58140       {a_variable:this.method_on_template(...)} - call a method on the template object.
58141  
58142 </code></pre>
58143  *  The tpl tag:
58144 <pre><code>
58145         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58146         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58147         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58148         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58149   
58150         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58151         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58152 </code></pre>
58153  *      
58154  */
58155 Roo.XTemplate = function()
58156 {
58157     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58158     if (this.html) {
58159         this.compile();
58160     }
58161 };
58162
58163
58164 Roo.extend(Roo.XTemplate, Roo.Template, {
58165
58166     /**
58167      * The various sub templates
58168      */
58169     tpls : false,
58170     /**
58171      *
58172      * basic tag replacing syntax
58173      * WORD:WORD()
58174      *
58175      * // you can fake an object call by doing this
58176      *  x.t:(test,tesT) 
58177      * 
58178      */
58179     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58180
58181     /**
58182      * compile the template
58183      *
58184      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58185      *
58186      */
58187     compile: function()
58188     {
58189         var s = this.html;
58190      
58191         s = ['<tpl>', s, '</tpl>'].join('');
58192     
58193         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58194             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58195             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58196             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58197             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58198             m,
58199             id     = 0,
58200             tpls   = [];
58201     
58202         while(true == !!(m = s.match(re))){
58203             var forMatch   = m[0].match(nameRe),
58204                 ifMatch   = m[0].match(ifRe),
58205                 execMatch   = m[0].match(execRe),
58206                 namedMatch   = m[0].match(namedRe),
58207                 
58208                 exp  = null, 
58209                 fn   = null,
58210                 exec = null,
58211                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58212                 
58213             if (ifMatch) {
58214                 // if - puts fn into test..
58215                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58216                 if(exp){
58217                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58218                 }
58219             }
58220             
58221             if (execMatch) {
58222                 // exec - calls a function... returns empty if true is  returned.
58223                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58224                 if(exp){
58225                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58226                 }
58227             }
58228             
58229             
58230             if (name) {
58231                 // for = 
58232                 switch(name){
58233                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58234                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58235                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58236                 }
58237             }
58238             var uid = namedMatch ? namedMatch[1] : id;
58239             
58240             
58241             tpls.push({
58242                 id:     namedMatch ? namedMatch[1] : id,
58243                 target: name,
58244                 exec:   exec,
58245                 test:   fn,
58246                 body:   m[1] || ''
58247             });
58248             if (namedMatch) {
58249                 s = s.replace(m[0], '');
58250             } else { 
58251                 s = s.replace(m[0], '{xtpl'+ id + '}');
58252             }
58253             ++id;
58254         }
58255         this.tpls = [];
58256         for(var i = tpls.length-1; i >= 0; --i){
58257             this.compileTpl(tpls[i]);
58258             this.tpls[tpls[i].id] = tpls[i];
58259         }
58260         this.master = tpls[tpls.length-1];
58261         return this;
58262     },
58263     /**
58264      * same as applyTemplate, except it's done to one of the subTemplates
58265      * when using named templates, you can do:
58266      *
58267      * var str = pl.applySubTemplate('your-name', values);
58268      *
58269      * 
58270      * @param {Number} id of the template
58271      * @param {Object} values to apply to template
58272      * @param {Object} parent (normaly the instance of this object)
58273      */
58274     applySubTemplate : function(id, values, parent)
58275     {
58276         
58277         
58278         var t = this.tpls[id];
58279         
58280         
58281         try { 
58282             if(t.test && !t.test.call(this, values, parent)){
58283                 return '';
58284             }
58285         } catch(e) {
58286             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58287             Roo.log(e.toString());
58288             Roo.log(t.test);
58289             return ''
58290         }
58291         try { 
58292             
58293             if(t.exec && t.exec.call(this, values, parent)){
58294                 return '';
58295             }
58296         } catch(e) {
58297             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58298             Roo.log(e.toString());
58299             Roo.log(t.exec);
58300             return ''
58301         }
58302         try {
58303             var vs = t.target ? t.target.call(this, values, parent) : values;
58304             parent = t.target ? values : parent;
58305             if(t.target && vs instanceof Array){
58306                 var buf = [];
58307                 for(var i = 0, len = vs.length; i < len; i++){
58308                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58309                 }
58310                 return buf.join('');
58311             }
58312             return t.compiled.call(this, vs, parent);
58313         } catch (e) {
58314             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58315             Roo.log(e.toString());
58316             Roo.log(t.compiled);
58317             return '';
58318         }
58319     },
58320
58321     compileTpl : function(tpl)
58322     {
58323         var fm = Roo.util.Format;
58324         var useF = this.disableFormats !== true;
58325         var sep = Roo.isGecko ? "+" : ",";
58326         var undef = function(str) {
58327             Roo.log("Property not found :"  + str);
58328             return '';
58329         };
58330         
58331         var fn = function(m, name, format, args)
58332         {
58333             //Roo.log(arguments);
58334             args = args ? args.replace(/\\'/g,"'") : args;
58335             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58336             if (typeof(format) == 'undefined') {
58337                 format= 'htmlEncode';
58338             }
58339             if (format == 'raw' ) {
58340                 format = false;
58341             }
58342             
58343             if(name.substr(0, 4) == 'xtpl'){
58344                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58345             }
58346             
58347             // build an array of options to determine if value is undefined..
58348             
58349             // basically get 'xxxx.yyyy' then do
58350             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58351             //    (function () { Roo.log("Property not found"); return ''; })() :
58352             //    ......
58353             
58354             var udef_ar = [];
58355             var lookfor = '';
58356             Roo.each(name.split('.'), function(st) {
58357                 lookfor += (lookfor.length ? '.': '') + st;
58358                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58359             });
58360             
58361             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58362             
58363             
58364             if(format && useF){
58365                 
58366                 args = args ? ',' + args : "";
58367                  
58368                 if(format.substr(0, 5) != "this."){
58369                     format = "fm." + format + '(';
58370                 }else{
58371                     format = 'this.call("'+ format.substr(5) + '", ';
58372                     args = ", values";
58373                 }
58374                 
58375                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58376             }
58377              
58378             if (args.length) {
58379                 // called with xxyx.yuu:(test,test)
58380                 // change to ()
58381                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58382             }
58383             // raw.. - :raw modifier..
58384             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58385             
58386         };
58387         var body;
58388         // branched to use + in gecko and [].join() in others
58389         if(Roo.isGecko){
58390             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58391                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58392                     "';};};";
58393         }else{
58394             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58395             body.push(tpl.body.replace(/(\r\n|\n)/g,
58396                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58397             body.push("'].join('');};};");
58398             body = body.join('');
58399         }
58400         
58401         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58402        
58403         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58404         eval(body);
58405         
58406         return this;
58407     },
58408
58409     applyTemplate : function(values){
58410         return this.master.compiled.call(this, values, {});
58411         //var s = this.subs;
58412     },
58413
58414     apply : function(){
58415         return this.applyTemplate.apply(this, arguments);
58416     }
58417
58418  });
58419
58420 Roo.XTemplate.from = function(el){
58421     el = Roo.getDom(el);
58422     return new Roo.XTemplate(el.value || el.innerHTML);
58423 };